-- v5: winebth.sys: Set additional properties for remote Bluetooth devices. bluetoothapis/tests: Implement tests for BluetoothGATTGetCharacteristics. bluetoothapis: Implement BluetoothGATTGetCharacteristics.
From: Vibhav Pant vibhavp@gmail.com
--- dlls/winebth.sys/dbus.c | 19 ++++++++++++++ dlls/winebth.sys/winebth.c | 45 +++++++++++++++++++++++++++++++++ dlls/winebth.sys/winebth_priv.h | 6 +++++ 3 files changed, 70 insertions(+)
diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index 08d63ceb307..5f736583f79 100644 --- a/dlls/winebth.sys/dbus.c +++ b/dlls/winebth.sys/dbus.c @@ -1677,6 +1677,22 @@ static DBusHandlerResult bluez_filter( DBusConnection *conn, DBusMessage *msg, v event )) unix_name_free( device ); } + else if (!strcmp( interfaces[i], BLUEZ_INTERFACE_GATT_SERVICE )) + { + struct unix_name *service; + union winebluetooth_watcher_event_data event; + + service = unix_name_get_or_create( object_path ); + if (!service) + { + ERR( "Failed to allocate memory for GATT service path %s\n", debugstr_a( object_path ) ); + continue; + } + event.gatt_service_removed.handle = (UINT_PTR)service; + if (!bluez_event_list_queue_new_event( event_list, BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_GATT_SERVICE_REMOVED, + event )) + unix_name_free( service ); + } } p_dbus_free_string_array( interfaces ); } @@ -1918,6 +1934,9 @@ static void bluez_watcher_free( struct bluez_watcher_ctx *watcher ) unix_name_free( (struct unix_name *)event1->event.gatt_service_added.device.handle ); unix_name_free( (struct unix_name *)event1->event.gatt_service_added.service.handle ); break; + case BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_GATT_SERVICE_REMOVED: + unix_name_free( (struct unix_name *)event1->event.gatt_service_removed.handle ); + break; } free( event1 ); } diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index bc880019740..637a080e470 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -1080,6 +1080,48 @@ static void bluetooth_device_add_gatt_service( struct winebluetooth_watcher_even winebluetooth_gatt_service_free( event.service ); }
+static void bluetooth_gatt_service_remove( winebluetooth_gatt_service_t service ) +{ + 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, service )) + { + list_remove( &svc->entry ); + LeaveCriticalSection( &device->props_cs ); + LeaveCriticalSection( &radio->remote_devices_cs ); + LeaveCriticalSection( &device_list_cs ); + winebluetooth_gatt_service_free( svc->service ); + free( svc ); + winebluetooth_gatt_service_free( service ); + return; + } + } + LeaveCriticalSection( &device->props_cs ); + } + LeaveCriticalSection( &radio->remote_devices_cs ); + } + LeaveCriticalSection( &device_list_cs ); + winebluetooth_gatt_service_free( service ); +} + static DWORD CALLBACK bluetooth_event_loop_thread_proc( void *arg ) { NTSTATUS status; @@ -1122,6 +1164,9 @@ static DWORD CALLBACK bluetooth_event_loop_thread_proc( void *arg ) case BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_GATT_SERVICE_ADDED: bluetooth_device_add_gatt_service( event->event_data.gatt_service_added ); break; + case BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_GATT_SERVICE_REMOVED: + bluetooth_gatt_service_remove( event->event_data.gatt_service_removed ); + 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 afacccdfb2c..6ba90f36615 100644 --- a/dlls/winebth.sys/winebth_priv.h +++ b/dlls/winebth.sys/winebth_priv.h @@ -222,6 +222,10 @@ NTSTATUS winebluetooth_auth_send_response( winebluetooth_device_t device, BLUETO NTSTATUS winebluetooth_device_start_pairing( winebluetooth_device_t device, IRP *irp );
void winebluetooth_gatt_service_free( winebluetooth_gatt_service_t service ); +static inline BOOL winebluetooth_gatt_service_equal( winebluetooth_gatt_service_t s1, winebluetooth_gatt_service_t s2) +{ + return s1.handle == s2.handle; +}
enum winebluetooth_watcher_event_type { @@ -233,6 +237,7 @@ enum winebluetooth_watcher_event_type BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_PROPERTIES_CHANGED, BLUETOOTH_WATCHER_EVENT_TYPE_PAIRING_FINISHED, BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_GATT_SERVICE_ADDED, + BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_GATT_SERVICE_REMOVED, };
struct winebluetooth_watcher_event_radio_added @@ -300,6 +305,7 @@ union winebluetooth_watcher_event_data struct winebluetooth_watcher_event_device_props_changed device_props_changed; 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
From: Vibhav Pant vibhavp@gmail.com
--- dlls/winebth.sys/dbus.c | 127 ++++++++++++++++++++++++++++++- dlls/winebth.sys/unixlib.c | 10 +++ dlls/winebth.sys/unixlib.h | 7 ++ dlls/winebth.sys/winebluetooth.c | 10 +++ dlls/winebth.sys/winebth.c | 88 +++++++++++++++++---- dlls/winebth.sys/winebth_priv.h | 46 +++++++++++ include/bthledef.h | 16 ++++ 7 files changed, 284 insertions(+), 20 deletions(-)
diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index 5f736583f79..c014654390a 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; }; @@ -1937,6 +1941,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 +1973,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 +2029,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 +2209,109 @@ 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 )) + { + struct unix_name *service_name = NULL, *char_name; + struct bluez_init_entry *init_entry; + BTH_LE_GATT_CHARACTERISTIC *props; + 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; + } + + props = &init_entry->object.characteristic.props; + 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_iter; + const struct { + const char *name; + BOOLEAN *flag; + } flags[] = { + { "broadcast", &props->IsBroadcastable }, + { "read", &props->IsReadable }, + { "write", &props->IsWritable }, + { "write-without-response", &props->IsWritableWithoutResponse }, + { "authenticate-signed-writes", &props->IsSignedWritable }, + { "notify", &props->IsNotifiable }, + { "indicate", &props->IsIndicatable }, + { "extended-properties", &props->HasExtendedProperties }, + }; + + p_dbus_message_iter_recurse( &variant, &flags_iter ); + while (p_dbus_message_iter_get_arg_type( &flags_iter ) != DBUS_TYPE_INVALID) + { + const char *flag_name; + SIZE_T i; + + p_dbus_message_iter_get_basic( &flags_iter, &flag_name ); + for (i = 0; i < ARRAY_SIZE( flags ); i++) + { + if (!strcmp( flags[i].name, flag_name )) + *flags[i].flag = TRUE; + } + p_dbus_message_iter_next( &flags_iter ); + } + } + 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; + GUID uuid; + + p_dbus_message_iter_get_basic( &variant, &uuid_str ); + if (parse_uuid( &uuid, uuid_str )) + uuid_to_le( &uuid, &props->CharacteristicUuid ); + else + 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, &props->AttributeHandle ); + } + 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 +2351,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 +2458,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..c9151b49f19 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 @@ -154,22 +164,6 @@ static NTSTATUS WINAPI dispatch_auth( DEVICE_OBJECT *device, IRP *irp ) return status; }
-static void uuid_to_le( const GUID *uuid, BTH_LE_UUID *le_uuid ) -{ - if (uuid->Data1 <= UINT16_MAX && uuid->Data2 == BTH_LE_ATT_BLUETOOTH_BASE_GUID.Data2 - && uuid->Data3 == BTH_LE_ATT_BLUETOOTH_BASE_GUID.Data3 - && !memcmp( uuid->Data4, BTH_LE_ATT_BLUETOOTH_BASE_GUID.Data4, sizeof( uuid->Data4 ) )) - { - le_uuid->IsShortUuid = TRUE; - le_uuid->Value.ShortUuid = uuid->Data1; - } - else - { - le_uuid->IsShortUuid = FALSE; - le_uuid->Value.LongUuid = *uuid; - } -} - static NTSTATUS bluetooth_remote_device_dispatch( DEVICE_OBJECT *device, struct bluetooth_remote_device *ext, IRP *irp ) { IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp ); @@ -1062,6 +1056,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 +1117,64 @@ 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; + + if (!(entry = calloc( 1, sizeof( *entry ) ))) + { + LeaveCriticalSection( &device->props_cs ); + LeaveCriticalSection( &radio->remote_devices_cs ); + goto failed; + } + + TRACE( "Adding GATT characteristic %#x under service %s for device %p\n", + characteristic.props.AttributeHandle, debugstr_guid( &svc->uuid ), + (void *)device->device.handle ); + + entry->characteristic = characteristic.characteristic; + entry->props = characteristic.props; + 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 +1220,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..e21b3b586d7 100644 --- a/dlls/winebth.sys/winebth_priv.h +++ b/dlls/winebth.sys/winebth_priv.h @@ -22,7 +22,10 @@ #ifndef __WINE_WINEBTH_WINEBTH_H_ #define __WINE_WINEBTH_WINEBTH_H_
+#include <stdint.h> + #include <bthsdpdef.h> +#include <bthledef.h> #include <bluetoothapis.h> #include <bthdef.h> #include <ddk/wdm.h> @@ -97,6 +100,24 @@ static inline const char *debugstr_minor_function_code( UCHAR code ) } #undef XX
+static inline void uuid_to_le( const GUID *uuid, BTH_LE_UUID *le_uuid ) +{ + const GUID le_base_uuid = { 0, 0, 0x1000, { 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb } }; + + if (uuid->Data1 <= UINT16_MAX && uuid->Data2 == le_base_uuid.Data2 + && uuid->Data3 == le_base_uuid.Data3 + && !memcmp( uuid->Data4, le_base_uuid.Data4, sizeof( uuid->Data4 ) )) + { + le_uuid->IsShortUuid = TRUE; + le_uuid->Value.ShortUuid = uuid->Data1; + } + else + { + le_uuid->IsShortUuid = FALSE; + le_uuid->Value.LongUuid = *uuid; + } +} + /* Undocumented device properties for Bluetooth radios. */ #define DEFINE_BTH_RADIO_DEVPROPKEY( d, i ) \ DEFINE_DEVPROPKEY( DEVPKEY_BluetoothRadio_##d, 0xa92f26ca, 0xeda7, 0x4b1d, 0x9d, 0xb2, 0x27, \ @@ -192,6 +213,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 +253,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 +266,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 +324,22 @@ 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; + BTH_LE_GATT_CHARACTERISTIC props; +}; + union winebluetooth_watcher_event_data { struct winebluetooth_watcher_event_radio_added radio_added; @@ -306,6 +351,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
From: Vibhav Pant vibhavp@gmail.com
--- dlls/winebth.sys/winebth.c | 84 +++++++++++++++++++++++++++++++++++++- include/wine/winebth.h | 9 ++++ 2 files changed, 92 insertions(+), 1 deletion(-)
diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index c9151b49f19..a4f68cfa37c 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -116,7 +116,8 @@ struct bluetooth_gatt_service unsigned int primary : 1; UINT16 handle;
- struct list characteristics; + CRITICAL_SECTION chars_cs; + struct list characteristics; /* Guarded by chars_cs */ };
struct bluetooth_gatt_characteristic @@ -164,6 +165,29 @@ static NTSTATUS WINAPI dispatch_auth( DEVICE_OBJECT *device, IRP *irp ) return status; }
+static void le_to_uuid( const BTH_LE_UUID *le_uuid, GUID *uuid ) +{ + if (le_uuid->IsShortUuid) + { + *uuid = BTH_LE_ATT_BLUETOOTH_BASE_GUID; + uuid->Data1 = le_uuid->Value.ShortUuid; + } + else + *uuid = le_uuid->Value.LongUuid; +} + +/* Caller should hold props_cs */ +static struct bluetooth_gatt_service *find_gatt_service( struct list *services, const GUID *uuid, UINT16 handle ) +{ + struct bluetooth_gatt_service *service; + LIST_FOR_EACH_ENTRY( service, services, struct bluetooth_gatt_service, entry ) + { + if (IsEqualGUID( &service->uuid, uuid ) && service->handle == handle) + return service; + } + return NULL; +} + static NTSTATUS bluetooth_remote_device_dispatch( DEVICE_OBJECT *device, struct bluetooth_remote_device *ext, IRP *irp ) { IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp ); @@ -216,6 +240,53 @@ static NTSTATUS bluetooth_remote_device_dispatch( DEVICE_OBJECT *device, struct status = STATUS_MORE_ENTRIES; break; } + case IOCTL_WINEBTH_LE_DEVICE_GET_GATT_CHARACTERISTICS: + { + const SIZE_T min_size = offsetof( struct winebth_le_device_get_gatt_characteristics_params, characteristics[0] ); + struct winebth_le_device_get_gatt_characteristics_params *chars = irp->AssociatedIrp.SystemBuffer; + struct bluetooth_gatt_characteristic *chrc; + struct bluetooth_gatt_service *service; + SIZE_T rem; + GUID uuid; + + if (!chars || outsize < min_size) + { + status = STATUS_INVALID_USER_BUFFER; + break; + } + + rem = (outsize - min_size)/sizeof( *chars->characteristics ); + status = STATUS_SUCCESS; + chars->count = 0; + le_to_uuid( &chars->service.ServiceUuid, &uuid ); + + EnterCriticalSection( &ext->props_cs ); + service = find_gatt_service( &ext->gatt_services, &uuid, chars->service.AttributeHandle ); + if (!service) + { + status = STATUS_INVALID_PARAMETER; + LeaveCriticalSection( &ext->props_cs ); + break; + } + + EnterCriticalSection( &service->chars_cs ); + LIST_FOR_EACH_ENTRY( chrc, &service->characteristics, struct bluetooth_gatt_characteristic, entry ) + { + chars->count++; + if (rem) + { + chars->characteristics[chars->count - 1] = chrc->props; + rem--; + } + } + LeaveCriticalSection( &service->chars_cs ); + LeaveCriticalSection( &ext->props_cs ); + + irp->IoStatus.Information = offsetof( struct winebth_le_device_get_gatt_characteristics_params, characteristics[chars->count] ); + if (chars->count > rem) + status = STATUS_MORE_ENTRIES; + break; + } default: FIXME( "Unimplemented IOCTL code: %#lx\n", code ); } @@ -1057,6 +1128,8 @@ static void bluetooth_device_add_gatt_service( struct winebluetooth_watcher_even service->handle = event.attr_handle; bluetooth_device_enable_le_iface( device ); list_init( &service->characteristics ); + InitializeCriticalSectionEx( &service->chars_cs, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO ); + service->chars_cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": bluetooth_gatt_service.chars_le");
EnterCriticalSection( &device->props_cs ); list_add_tail( &device->gatt_services, &service->entry ); @@ -1099,11 +1172,20 @@ static void bluetooth_gatt_service_remove( winebluetooth_gatt_service_t service { if (winebluetooth_gatt_service_equal( svc->service, service )) { + struct bluetooth_gatt_characteristic *cur, *next; + list_remove( &svc->entry ); LeaveCriticalSection( &device->props_cs ); LeaveCriticalSection( &radio->remote_devices_cs ); LeaveCriticalSection( &device_list_cs ); winebluetooth_gatt_service_free( svc->service ); + svc->chars_cs.DebugInfo->Spare[0] = 0; + DeleteCriticalSection( &svc->chars_cs ); + LIST_FOR_EACH_ENTRY_SAFE( cur, next, &svc->characteristics, struct bluetooth_gatt_characteristic, entry ) + { + winebluetooth_gatt_characteristic_free( cur->characteristic ); + free( cur ); + } free( svc ); winebluetooth_gatt_service_free( service ); return; diff --git a/include/wine/winebth.h b/include/wine/winebth.h index 6a2893d6474..572be91bb45 100644 --- a/include/wine/winebth.h +++ b/include/wine/winebth.h @@ -38,6 +38,8 @@
/* Get all primary GATT services for the LE device. */ #define IOCTL_WINEBTH_LE_DEVICE_GET_GATT_SERVICES CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xc0, METHOD_BUFFERED, FILE_ANY_ACCESS) +/* Get all characteristics for a GATT service */ +#define IOCTL_WINEBTH_LE_DEVICE_GET_GATT_CHARACTERISTICS CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xc1, METHOD_BUFFERED, FILE_ANY_ACCESS)
DEFINE_GUID( GUID_WINEBTH_AUTHENTICATION_REQUEST, 0xca67235f, 0xf621, 0x4c27, 0x85, 0x65, 0xa4, 0xd5, 0x5e, 0xa1, 0x26, 0xe8 ); @@ -84,6 +86,13 @@ struct winebth_le_device_get_gatt_services_params BTH_LE_GATT_SERVICE services[0]; };
+struct winebth_le_device_get_gatt_characteristics_params +{ + BTH_LE_GATT_SERVICE service; + ULONG count; + BTH_LE_GATT_CHARACTERISTIC characteristics[0]; +}; + #pragma pack(pop)
#endif /* __WINEBTH_H__ */
From: Vibhav Pant vibhavp@gmail.com
--- dlls/winebth.sys/dbus.c | 19 ++++++++++++ dlls/winebth.sys/winebth.c | 53 +++++++++++++++++++++++++++++++++ dlls/winebth.sys/winebth_priv.h | 7 +++++ 3 files changed, 79 insertions(+)
diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index c014654390a..0bc03d24a66 100644 --- a/dlls/winebth.sys/dbus.c +++ b/dlls/winebth.sys/dbus.c @@ -1697,6 +1697,22 @@ static DBusHandlerResult bluez_filter( DBusConnection *conn, DBusMessage *msg, v event )) unix_name_free( service ); } + else if (!strcmp( interfaces[i], BLUEZ_INTERFACE_GATT_CHARACTERISTICS )) + { + struct unix_name *chrc; + union winebluetooth_watcher_event_data event; + + chrc = unix_name_get_or_create( object_path ); + if (!chrc) + { + ERR( "Failed to allocate memory for GATT characteristic path %s\n", debugstr_a( object_path ) ); + continue; + } + event.gatt_characterisic_removed.handle = (UINT_PTR)chrc; + if (!bluez_event_list_queue_new_event( event_list, BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_REMOVED, + event )) + unix_name_free( chrc ); + } } p_dbus_free_string_array( interfaces ); } @@ -1945,6 +1961,9 @@ static void bluez_watcher_free( struct bluez_watcher_ctx *watcher ) 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; + case BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_REMOVED: + unix_name_free( (struct unix_name *)event1->event.gatt_characterisic_removed.handle ); + break; } free( event1 ); } diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index a4f68cfa37c..e8fe328e18a 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -1257,6 +1257,56 @@ failed: winebluetooth_gatt_service_free( characteristic.service ); }
+static void bluetooth_gatt_characteristic_remove( winebluetooth_gatt_characteristic_t handle ) +{ + 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 ) + { + struct bluetooth_gatt_characteristic *chrc; + EnterCriticalSection( &svc->chars_cs ); + LIST_FOR_EACH_ENTRY( chrc, &svc->characteristics, struct bluetooth_gatt_characteristic, entry ) + { + if (winebluetooth_gatt_characteristic_equal( chrc->characteristic, handle )) + { + list_remove( &chrc->entry ); + LeaveCriticalSection( &svc->chars_cs ); + LeaveCriticalSection( &device->props_cs ); + LeaveCriticalSection( &radio->remote_devices_cs ); + LeaveCriticalSection( &device_list_cs ); + + winebluetooth_gatt_characteristic_free( chrc->characteristic ); + winebluetooth_gatt_characteristic_free( handle ); + free( chrc ); + return; + } + } + LeaveCriticalSection( &svc->chars_cs ); + } + LeaveCriticalSection( &device->props_cs ); + } + LeaveCriticalSection( &radio->remote_devices_cs ); + } + LeaveCriticalSection( &device_list_cs ); + winebluetooth_gatt_characteristic_free( handle ); +} + static DWORD CALLBACK bluetooth_event_loop_thread_proc( void *arg ) { NTSTATUS status; @@ -1305,6 +1355,9 @@ static DWORD CALLBACK bluetooth_event_loop_thread_proc( void *arg ) case BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_ADDED: bluetooth_gatt_service_add_characteristic( event->event_data.gatt_characteristic_added ); break; + case BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_REMOVED: + bluetooth_gatt_characteristic_remove( event->event_data.gatt_characterisic_removed ); + 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 e21b3b586d7..34582ed3759 100644 --- a/dlls/winebth.sys/winebth_priv.h +++ b/dlls/winebth.sys/winebth_priv.h @@ -254,6 +254,11 @@ static inline BOOL winebluetooth_gatt_service_equal( winebluetooth_gatt_service_ }
void winebluetooth_gatt_characteristic_free( winebluetooth_gatt_characteristic_t characteristic ); +static inline BOOL winebluetooth_gatt_characteristic_equal( winebluetooth_gatt_characteristic_t c1, + winebluetooth_gatt_characteristic_t c2) +{ + return c1.handle == c2.handle; +}
enum winebluetooth_watcher_event_type { @@ -267,6 +272,7 @@ enum winebluetooth_watcher_event_type BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_GATT_SERVICE_ADDED, BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_GATT_SERVICE_REMOVED, BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_ADDED, + BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_REMOVED, };
struct winebluetooth_watcher_event_radio_added @@ -352,6 +358,7 @@ union winebluetooth_watcher_event_data 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; + winebluetooth_gatt_characteristic_t gatt_characterisic_removed; };
struct winebluetooth_watcher_event
From: Vibhav Pant vibhavp@gmail.com
--- dlls/bluetoothapis/bluetoothapis.spec | 2 +- dlls/bluetoothapis/gatt.c | 65 ++++++++++++++++++++++++++- include/bluetoothleapis.h | 1 + 3 files changed, 65 insertions(+), 3 deletions(-)
diff --git a/dlls/bluetoothapis/bluetoothapis.spec b/dlls/bluetoothapis/bluetoothapis.spec index 2cc00f46c9d..9d171a18013 100644 --- a/dlls/bluetoothapis/bluetoothapis.spec +++ b/dlls/bluetoothapis/bluetoothapis.spec @@ -35,7 +35,7 @@ @ stub BluetoothGATTBeginReliableWrite @ stub BluetoothGATTEndReliableWrite @ stub BluetoothGATTGetCharacteristicValue -@ stub BluetoothGATTGetCharacteristics +@ stdcall BluetoothGATTGetCharacteristics(ptr ptr long ptr ptr long) @ stub BluetoothGATTGetDescriptorValue @ stub BluetoothGATTGetDescriptors @ stub BluetoothGATTGetIncludedServices diff --git a/dlls/bluetoothapis/gatt.c b/dlls/bluetoothapis/gatt.c index 12b3ad00f78..e6b2a90fab4 100644 --- a/dlls/bluetoothapis/gatt.c +++ b/dlls/bluetoothapis/gatt.c @@ -38,6 +38,20 @@
WINE_DEFAULT_DEBUG_CHANNEL( bluetoothapis );
+static const char *debugstr_BTH_LE_UUID( const BTH_LE_UUID *uuid ) +{ + if (uuid->IsShortUuid) + return wine_dbg_sprintf("{ IsShortUuid=1 {%#x} }", uuid->Value.ShortUuid ); + return wine_dbg_sprintf( "{ IsShortUuid=0 %s }", debugstr_guid( &uuid->Value.LongUuid ) ); +} + +static const char *debugstr_BTH_LE_GATT_SERVICE( const BTH_LE_GATT_SERVICE *svc ) +{ + if (!svc) + return wine_dbg_sprintf( "(null)" ); + return wine_dbg_sprintf( "{ %s %#x }", debugstr_BTH_LE_UUID( &svc->ServiceUuid ), svc->AttributeHandle ); +} + HRESULT WINAPI BluetoothGATTGetServices( HANDLE le_device, USHORT count, BTH_LE_GATT_SERVICE *buf, USHORT *actual, ULONG flags ) { @@ -49,7 +63,7 @@ HRESULT WINAPI BluetoothGATTGetServices( HANDLE le_device, USHORT count, BTH_LE_ if (!actual) return E_POINTER;
- if ((!buf && count) || (buf && !count) || !actual) + if ((!buf && count) || (buf && !count)) return E_INVALIDARG;
for (;;) @@ -88,10 +102,57 @@ HRESULT WINAPI BluetoothGATTGetServices( HANDLE le_device, USHORT count, BTH_LE_ return HRESULT_FROM_WIN32( ERROR_MORE_DATA ); }
- memcpy( buf, services->services, min( services_count, count ) ); + memcpy( buf, services->services, min( services_count, count ) * sizeof( *buf ) ); free( services ); if (count < services_count) return HRESULT_FROM_WIN32( ERROR_INVALID_USER_BUFFER );
return S_OK; } + +HRESULT WINAPI BluetoothGATTGetCharacteristics( HANDLE device, BTH_LE_GATT_SERVICE *service, USHORT count, + BTH_LE_GATT_CHARACTERISTIC *buf, USHORT *actual, ULONG flags ) +{ + struct winebth_le_device_get_gatt_characteristics_params *chars; + DWORD size, bytes; + + TRACE( "(%p, %s, %u, %p, %p, %#lx)\n", device, debugstr_BTH_LE_GATT_SERVICE( service ), count, buf, actual, flags ); + + if (flags) + FIXME( "Unsupported flags: %#lx\n", flags ); + + if (!actual) + return E_POINTER; + + if ((buf && !count) || !service) + return E_INVALIDARG; + + size = offsetof( struct winebth_le_device_get_gatt_characteristics_params, characteristics[count] ); + chars = calloc( 1, size ); + if (!chars) + return HRESULT_FROM_WIN32( ERROR_NO_SYSTEM_RESOURCES ); + chars->service = *service; + if (!DeviceIoControl( device, IOCTL_WINEBTH_LE_DEVICE_GET_GATT_CHARACTERISTICS, chars, size, chars, + size, &bytes, NULL ) && GetLastError() != ERROR_MORE_DATA) + { + free( chars ); + return HRESULT_FROM_WIN32( GetLastError() ); + } + + *actual = chars->count; + if (!chars->count) + { + free( chars ); + return S_OK; + } + if (!buf) + { + free( chars ); + return HRESULT_FROM_WIN32( ERROR_MORE_DATA ); + } + memcpy( buf, chars->characteristics, min( count, chars->count ) * sizeof( *buf ) ); + if (count < chars->count) + return HRESULT_FROM_WIN32( ERROR_INVALID_USER_BUFFER ); + free( chars ); + return S_OK; +} diff --git a/include/bluetoothleapis.h b/include/bluetoothleapis.h index 64cdd366f7d..e790eb32aa4 100644 --- a/include/bluetoothleapis.h +++ b/include/bluetoothleapis.h @@ -25,6 +25,7 @@ extern "C" { #endif
HRESULT WINAPI BluetoothGATTGetServices( HANDLE, USHORT, BTH_LE_GATT_SERVICE *, USHORT *, ULONG ); +HRESULT WINAPI BluetoothGATTGetCharacteristics( HANDLE, BTH_LE_GATT_SERVICE *, USHORT, BTH_LE_GATT_CHARACTERISTIC *, USHORT *, ULONG );
#ifdef __cplusplus }
From: Vibhav Pant vibhavp@gmail.com
--- dlls/bluetoothapis/tests/gatt.c | 81 +++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 5 deletions(-)
diff --git a/dlls/bluetoothapis/tests/gatt.c b/dlls/bluetoothapis/tests/gatt.c index 0afe1438141..3b3f26b2418 100644 --- a/dlls/bluetoothapis/tests/gatt.c +++ b/dlls/bluetoothapis/tests/gatt.c @@ -88,8 +88,6 @@ static void test_for_all_le_devices( int line, void (*func)( HANDLE, void * ), v
static const char *debugstr_BTH_LE_UUID( const BTH_LE_UUID *uuid ) { - if (!uuid) - return wine_dbg_sprintf( "(null)" ); if (uuid->IsShortUuid) return wine_dbg_sprintf("{ IsShortUuid=1 {%#x} }", uuid->Value.ShortUuid ); return wine_dbg_sprintf( "{ IsShortUuid=0 %s }", debugstr_guid( &uuid->Value.LongUuid ) ); @@ -97,11 +95,44 @@ static const char *debugstr_BTH_LE_UUID( const BTH_LE_UUID *uuid )
static const char *debugstr_BTH_LE_GATT_SERVICE( const BTH_LE_GATT_SERVICE *svc ) { - if (!svc) - return wine_dbg_sprintf( "(null)" ); return wine_dbg_sprintf( "{ %s %#x }", debugstr_BTH_LE_UUID( &svc->ServiceUuid ), svc->AttributeHandle ); }
+static const char *debugstr_BTH_LE_GATT_CHARACTERISTIC( const BTH_LE_GATT_CHARACTERISTIC *chrc ) +{ + return wine_dbg_sprintf( "{ %#x %s %#x %#x %d %d %d %d %d %d %d %d}", chrc->ServiceHandle, + debugstr_BTH_LE_UUID( &chrc->CharacteristicUuid ), chrc->AttributeHandle, + chrc->CharacteristicValueHandle, chrc->IsBroadcastable, chrc->IsReadable, chrc->IsWritable, + chrc->IsWritableWithoutResponse, chrc->IsSignedWritable, chrc->IsNotifiable, + chrc->IsIndicatable, chrc->HasExtendedProperties ); +} + +static void test_service_BluetoothGATTGetCharacteristics( HANDLE device, BTH_LE_GATT_SERVICE *service ) +{ + HRESULT ret; + USHORT actual = 0, actual2 = 0, i; + BTH_LE_GATT_CHARACTERISTIC *buf = NULL, dummy; + + ret = BluetoothGATTGetCharacteristics( device, service, 0, &dummy, &actual, 0 ); + ok( ret == E_INVALIDARG, "got ret %#lx\n", ret ); + + ret = BluetoothGATTGetCharacteristics( device, service, 0, NULL, &actual, 0 ); + ok( ret == HRESULT_FROM_WIN32( ERROR_MORE_DATA ), "got ret %#lx\n", ret ); + + ret = BluetoothGATTGetCharacteristics( device, service, actual, NULL, &actual2, 0 ); + ok( ret == HRESULT_FROM_WIN32( ERROR_MORE_DATA ), "got ret %#lx\n", ret ); + ok( actual == actual2, "%u != %u\n", actual, actual2 ); + + buf = calloc( actual, sizeof( *buf ) ); + ret = BluetoothGATTGetCharacteristics( device, service, actual, buf, &actual, 0 ); + ok( ret == S_OK, "BluetoothGATTGetCharacteristics failed: %#lx\n", ret ); + + for (i = 0; i < actual; i++) + trace( "characteristic %u: %s\n", i, debugstr_BTH_LE_GATT_CHARACTERISTIC( &buf[i] ) ); + + free( buf ); +} + static void test_device_BluetoothGATTGetServices( HANDLE device, void *param ) { HRESULT ret; @@ -141,7 +172,12 @@ static void test_device_BluetoothGATTGetServices( HANDLE device, void *param ) }
for (i = 0; i < actual; i++) - trace( "service %u: %s\n", i, debugstr_BTH_LE_GATT_SERVICE( &buf[i] ) ); + { + winetest_push_context( "service %u", i ); + trace( "%s\n", debugstr_BTH_LE_GATT_SERVICE( &buf[i] ) ); + test_service_BluetoothGATTGetCharacteristics( device, &buf[i] ); + winetest_pop_context(); + }
free( buf ); } @@ -156,7 +192,42 @@ static void test_BluetoothGATTGetServices( void ) test_for_all_le_devices( __LINE__, test_device_BluetoothGATTGetServices, NULL ); }
+static void test_device_BluetoothGATTGetCharacteristics( HANDLE device, void *data ) +{ + HRESULT ret; + USHORT actual = 0; + BTH_LE_GATT_SERVICE svc = {.ServiceUuid = {TRUE, {.ShortUuid = 0xdead}}, 0xbeef}; + + ret = BluetoothGATTGetCharacteristics( device, NULL, 0, NULL, NULL, 0 ); + ok( ret == E_POINTER, "got ret %#lx\n", ret ); + + ret = BluetoothGATTGetCharacteristics( device, &svc, 0, NULL, NULL, 0 ); + ok( ret == E_POINTER, "got ret %#lx\n", ret ); + + ret = BluetoothGATTGetCharacteristics( device, &svc, 0, NULL, &actual, 0 ); + ok( ret == E_INVALIDARG, "got ret %#lx\n", ret ); +} + +static void test_BluetoothGATTGetCharacteristic( void ) +{ + HRESULT ret; + USHORT actual = 0; + BTH_LE_GATT_SERVICE svc; + + ret = BluetoothGATTGetCharacteristics( NULL, NULL, 0, NULL, NULL, 0 ); + ok( ret == E_POINTER, "got ret %#lx\n", ret ); + + ret = BluetoothGATTGetCharacteristics( NULL, &svc, 0, NULL, &actual, 0 ); + ok( ret == E_HANDLE, "got ret %#lx\n", ret ); + + ret = BluetoothGATTGetCharacteristics( INVALID_HANDLE_VALUE, &svc, 0, NULL, &actual, 0 ); + ok( ret == E_HANDLE, "got ret %#lx\n", ret ); + + test_for_all_le_devices( __LINE__, test_device_BluetoothGATTGetCharacteristics, NULL ); +} + START_TEST( gatt ) { test_BluetoothGATTGetServices(); + test_BluetoothGATTGetCharacteristic(); }
From: Vibhav Pant vibhavp@gmail.com
--- dlls/winebth.sys/winebth.c | 51 ++++++++++++++++++++++++++++++++------ include/ddk/bthguid.h | 6 +++++ 2 files changed, 49 insertions(+), 8 deletions(-)
diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index e8fe328e18a..47ed062725a 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -29,8 +29,10 @@ #include <winbase.h> #include <winternl.h> #include <winnls.h> +#include <wtypes.h> #include <initguid.h> #include <devpkey.h> +#include <propkey.h> #include <bthsdpdef.h> #include <bluetoothapis.h> #include <bthdef.h> @@ -947,6 +949,45 @@ static void bluetooth_radio_remove_remote_device( struct winebluetooth_watcher_e winebluetooth_device_free( event.device ); }
+/* Caller should hold device->props_cs. */ +static void bluetooth_device_set_properties( struct bluetooth_remote_device *device, + const struct winebluetooth_device_properties *props, + winebluetooth_device_props_mask_t mask ) +{ + WCHAR addr_str[13]; + + /* Windows uses upper-case for Bluetooth_DeviceAddress. */ + swprintf( addr_str, ARRAY_SIZE( addr_str ), L"%02X%02X%02X%02X%02X%02X", props->address.rgBytes[0], props->address.rgBytes[1], + props->address.rgBytes[2], props->address.rgBytes[3], props->address.rgBytes[4], props->address.rgBytes[5] ); + + if (mask & WINEBLUETOOTH_DEVICE_PROPERTY_ADDRESS) + { + IoSetDevicePropertyData( device->device_obj, &DEVPKEY_Bluetooth_DeviceAddress, LOCALE_NEUTRAL, 0, + DEVPROP_TYPE_STRING, sizeof( addr_str ), addr_str ); + if (device->bthle_symlink_name.Buffer) + IoSetDeviceInterfacePropertyData( &device->bthle_symlink_name, + (DEVPROPKEY *)&PKEY_DeviceInterface_Bluetooth_DeviceAddress, + LOCALE_NEUTRAL, 0, DEVPROP_TYPE_STRING, sizeof( addr_str ), addr_str ); + } + if (mask & WINEBLUETOOTH_DEVICE_PROPERTY_CLASS) + { + IoSetDevicePropertyData( device->device_obj, &DEVPKEY_Bluetooth_ClassOfDevice, LOCALE_NEUTRAL, 0, + DEVPROP_TYPE_UINT32, sizeof( props->class ), (void *)&props->class ); + } + if (mask & WINEBLUETOOTH_DEVICE_PROPERTY_CONNECTED && props->connected) + { + FILETIME time = {0}; + + GetSystemTimeAsFileTime( &time ); + IoSetDevicePropertyData( device->device_obj, &DEVPKEY_Bluetooth_LastConnectedTime, LOCALE_NEUTRAL, 0, + DEVPROP_TYPE_FILETIME, sizeof( time ), (void *)&time ); + if (device->bthle_symlink_name.Buffer) + IoSetDeviceInterfacePropertyData( &device->bthle_symlink_name, + (DEVPROPKEY *)&PKEY_DeviceInterface_Bluetooth_LastConnectedTime, + LOCALE_NEUTRAL, 0, DEVPROP_TYPE_FILETIME, sizeof( time ), (void *)&time ); + } +} + static void bluetooth_radio_update_device_props( struct winebluetooth_watcher_event_device_props_changed event ) { BTH_DEVICE_INFO device_new_info = {0}; @@ -988,6 +1029,7 @@ static void bluetooth_radio_update_device_props( struct winebluetooth_watcher_ev if (event.changed_props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_CLASS) device->props.class = event.props.class; winebluetooth_device_properties_to_info( device->props_mask, &device->props, &device_new_info ); + bluetooth_device_set_properties( device, &device->props, device->props_mask ); LeaveCriticalSection( &device->props_cs ); LeaveCriticalSection( &radio->remote_devices_cs );
@@ -1616,21 +1658,14 @@ static NTSTATUS WINAPI remote_device_pdo_pnp( DEVICE_OBJECT *device_obj, struct } case IRP_MN_START_DEVICE: { - WCHAR addr_str[13]; - BLUETOOTH_ADDRESS addr; - EnterCriticalSection( &ext->props_cs ); if (ext->le && !IoRegisterDeviceInterface( device_obj, &GUID_BLUETOOTHLE_DEVICE_INTERFACE, NULL, &ext->bthle_symlink_name )) IoSetDeviceInterfaceState( &ext->bthle_symlink_name, TRUE ); ext->started = TRUE; - addr = ext->props.address; + bluetooth_device_set_properties( ext, &ext->props, ext->props_mask ); LeaveCriticalSection( &ext->props_cs ); - swprintf( addr_str, ARRAY_SIZE( addr_str ), L"%02x%02x%02x%02x%02x%02x", addr.rgBytes[0], addr.rgBytes[1], - addr.rgBytes[2], addr.rgBytes[3], addr.rgBytes[4], addr.rgBytes[5] ); - IoSetDevicePropertyData( device_obj, &DEVPKEY_Bluetooth_DeviceAddress, LOCALE_NEUTRAL, 0, DEVPROP_TYPE_STRING, - sizeof( addr_str ), addr_str ); ret = STATUS_SUCCESS; break; } diff --git a/include/ddk/bthguid.h b/include/ddk/bthguid.h index d042e13a37b..d8122263cb8 100644 --- a/include/ddk/bthguid.h +++ b/include/ddk/bthguid.h @@ -21,5 +21,11 @@
/* DEVPROP_TYPE_STRING */ DEFINE_DEVPROPKEY( DEVPKEY_Bluetooth_DeviceAddress, 0x2bd67d8b,0x8beb,0x48d5,0x87,0xe0,0x6c,0xda,0x34,0x28,0x04,0x0a,1 ); +/* DEVPROP_TYPE_UINT32 */ +DEFINE_DEVPROPKEY( DEVPKEY_Bluetooth_DeviceFlags, 0x2bd67d8b,0x8beb,0x48d5,0x87,0xe0,0x6c,0xda,0x34,0x28,0x04,0x0a,3 ); +/* DEVPROP_TYPE_UINT32 */ +DEFINE_DEVPROPKEY( DEVPKEY_Bluetooth_ClassOfDevice, 0x2bd67d8b,0x8beb,0x48d5,0x87,0xe0,0x6c,0xda,0x34,0x28,0x04,0x0a,10 ); +/* DEVPROP_TYPE_FILETIME */ +DEFINE_DEVPROPKEY( DEVPKEY_Bluetooth_LastConnectedTime, 0x2bd67d8b,0x8beb,0x48d5,0x87,0xe0,0x6c,0xda,0x34,0x28,0x04,0x0a,11 );
#endif
Maybe embed BTH_LE_GATT_CHARACTERISTIC inside struct winebluetooth_watcher_event_gatt_characteristic_added, then, and just add the extra parts alongside it later.
Makes sense, I've tried to do that in the latest revision, thanks.
And how about the latter question? Do we really get a performance difference from separating all these locks?
So I think apart from the per-device locks, we could perhaps merge `device_list_cs`, `bluetooth_radio.props_cs`, and `remote_devices_cs`. I'll take a stab at this at some point.
I understand the loop pattern, but why do you need to make more than _one_ ioctl? Unlike in BluetoothFindFirstDevice(), you don't need to retrieve every characteristic here, only as many as the caller allocated space for.
Ah, I get what you mean. Yeah, we don't need to necessarily get _all_ characteristics, I missed this. Doing it once should be enough to inform the user if there are more characteristics or not. I have simplified the function in the latest revision (and will do the same for BluetoothGATTGetServices in the next MR)
Sure, but MSDN isn't always accurate, and it might change in the future without us really knowing about it. I always advocate for a FIXME for any parameter we ignore, even if it's documented as being reserved. Especially if we aren't going to check for it and return an error.
Sure. I have added a FIXME for this, thanks.
v4, v5:
* Simplify `BluetoothGATTGetCharacteristics`. * Use `BTH_LE_GATT_CHARACTERISTIC` in `winebluetooth_watcher_event_gatt_characteristic_added`