[PATCH 0/7] MR10099: winebth.sys part 14: Create PDOs for GATT services.
<span dir="">GATT services are exposed with the </span>`GUID_BLUETOOTH_GATT_SERVICE_DEVICE_INTERFACE`<span dir=""> interface, and are needed to implement methods like </span>[`BluetoothGATTGetCharacteristicValue`](https://learn.microsoft.com/en-us/windows/win32/api/bluetoothleapis/nf-bluetoothleapis-bluetoothgattgetcharacteristicvalue)<span dir=""> (the first argument needs to be a handle to a service). Additionally, methods like </span>[`BluetoothGATTGetCharacteristics`](https://learn.microsoft.com/en-us/windows/win32/api/bluetoothleapis/nf-bluetoothleapis-bluetoothgattgetcharacteristics)<span dir=""> accept </span>`HANDLE`<span dir="">s to both LE devices and services as the first argument.</span> -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10099
From: Vibhav Pant <vibhavp@gmail.com> --- dlls/winebth.sys/dbus.c | 113 ++++++++++++++++++++++++++-------------- 1 file changed, 74 insertions(+), 39 deletions(-) diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index f1583280c67..2db38c1010c 100644 --- a/dlls/winebth.sys/dbus.c +++ b/dlls/winebth.sys/dbus.c @@ -806,6 +806,41 @@ static void bluez_device_prop_from_dict_entry( const char *prop_name, DBusMessag } } +static void bluez_gatt_service_props_from_dict_entry( const char *prop_name, DBusMessageIter *variant, + struct winebluetooth_watcher_event_gatt_service_added *service ) +{ + TRACE_( dbus )( "(%s, %p, %p)\n", debugstr_a( prop_name ), variant, service ); + + if (!strcmp( prop_name, "Device" ) + && p_dbus_message_iter_get_arg_type( variant ) == DBUS_TYPE_OBJECT_PATH ) + { + const char *device_path; + struct unix_name *device_name; + + p_dbus_message_iter_get_basic( variant, &device_path ); + device_name = unix_name_get_or_create( device_path ); + service->device.handle = (UINT_PTR)device_name; + } + else if (!strcmp( prop_name, "Handle" ) + && p_dbus_message_iter_get_arg_type( variant ) == DBUS_TYPE_UINT16) + p_dbus_message_iter_get_basic( variant, &service->attr_handle ); + else if (!strcmp( prop_name, "Primary" ) + && p_dbus_message_iter_get_arg_type( variant ) == DBUS_TYPE_BOOLEAN) + { + dbus_bool_t primary; + p_dbus_message_iter_get_basic( variant, &primary ); + service->is_primary = !!primary; + } + 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( &service->uuid, uuid_str )) + ERR( "Failed to parse UUID %s\n", debugstr_a( uuid_str ) ); + } +} + static NTSTATUS bluez_adapter_get_props_async( void *connection, const char *radio_object_path, DBusPendingCall **call ) { @@ -1625,6 +1660,41 @@ static void bluez_signal_handler( DBusConnection *conn, DBusMessage *msg, const } } } + else if (!strcmp( iface_name, BLUEZ_INTERFACE_GATT_SERVICE )) + { + union winebluetooth_watcher_event_data event = {0}; + DBusMessageIter props_iter, variant; + struct unix_name *service_name; + const char *prop_name; + + if (!(service_name = unix_name_get_or_create( object_path ))) + { + ERR( "Failed to allocate memory for GATT service path %s\n", debugstr_a( object_path ) ); + break; + } + event.gatt_service_added.service.handle = (UINT_PTR)service_name; + p_dbus_message_iter_next( &iface_entry ); + p_dbus_message_iter_recurse( &iface_entry, &props_iter ); + + while ((prop_name = bluez_next_dict_entry( &props_iter, &variant ))) + bluez_gatt_service_props_from_dict_entry( prop_name, &variant, &event.gatt_service_added ); + + if (!event.gatt_service_added.device.handle) + { + unix_name_free( service_name ); + ERR( "Could not find the associated device for the GATT service %s\n", debugstr_a( object_path ) ); + break; + } + + TRACE( "New Bluez org.bluez.GattService1 object added at %s: %p\n", debugstr_a( object_path ), + service_name ); + if (!bluez_event_list_queue_new_event( event_list, BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_GATT_SERVICE_ADDED, + event )) + { + unix_name_free( service_name ); + unix_name_free( (struct unix_name *)event.gatt_service_added.device.handle ); + } + } p_dbus_message_iter_next( &ifaces_iter ); } } @@ -2178,8 +2248,8 @@ static NTSTATUS bluez_build_initial_device_lists( DBusMessage *reply, struct lis } else if (!strcmp( iface, BLUEZ_INTERFACE_GATT_SERVICE )) { - struct unix_name *service_name, *device_name = NULL; struct bluez_init_entry *init_device; + struct unix_name *service_name; DBusMessageIter variant; const char *prop_name; @@ -2194,49 +2264,14 @@ static NTSTATUS bluez_build_initial_device_lists( DBusMessage *reply, struct lis if (!service_name) { ERR( "Failed to allocate memory for service path %s\n", debugstr_a( path ) ); + free( init_device ); break; } init_device->object.service.service.handle = (UINT_PTR)service_name; while ((prop_name = bluez_next_dict_entry( &prop_iter, &variant ))) - { - if (!strcmp( prop_name, "Device" ) - && p_dbus_message_iter_get_arg_type( &variant ) == DBUS_TYPE_OBJECT_PATH ) - { - const char *device_path; - - p_dbus_message_iter_get_basic( &variant, &device_path ); - device_name = unix_name_get_or_create( device_path ); - if (!device_name) - { - unix_name_free( service_name ); - free( init_device ); - ERR( "Failed to allocate memory for device path %s\n", debugstr_a( device_path )); - status = STATUS_NO_MEMORY; - goto done; - } - init_device->object.service.device.handle = (UINT_PTR)device_name; - } - 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_device->object.service.attr_handle ); - else if (!strcmp( prop_name, "Primary" ) - && p_dbus_message_iter_get_arg_type( &variant ) == DBUS_TYPE_BOOLEAN) - { - dbus_bool_t primary; - p_dbus_message_iter_get_basic( &variant, &primary ); - init_device->object.service.is_primary = !!primary; - } - 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_device->object.service.uuid, uuid_str )) - ERR("Failed to parse UUID %s for GATT service %s\n", debugstr_a( uuid_str ), debugstr_a( path ) ); - } - } - if (!device_name) + bluez_gatt_service_props_from_dict_entry( prop_name, &variant, &init_device->object.service ); + if (!init_device->object.service.device.handle) { unix_name_free( service_name ); free( init_device ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10099
From: Vibhav Pant <vibhavp@gmail.com> --- dlls/winebth.sys/dbus.c | 179 ++++++++++++++++++++++++++-------------- 1 file changed, 115 insertions(+), 64 deletions(-) diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index 2db38c1010c..44098f1abd1 100644 --- a/dlls/winebth.sys/dbus.c +++ b/dlls/winebth.sys/dbus.c @@ -841,6 +841,80 @@ static void bluez_gatt_service_props_from_dict_entry( const char *prop_name, DBu } } +struct named_flag +{ + const char *name; + BOOLEAN *flag; +}; + +static int named_flag_cmp( const void *flag1, const void *flag2 ) +{ + const struct named_flag *f1 = flag1, *f2 = flag2; + return strcmp( f1->name, f2->name ); +} + +static void +bluez_gatt_characteristic_props_from_dict_entry( const char *prop_name, DBusMessageIter *variant, + struct winebluetooth_watcher_event_gatt_characteristic_added *chrc ) +{ + TRACE_( dbus )( "(%s, %p, %p)\n", debugstr_a( prop_name ), variant, chrc ); + + 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) + { + /* These should always be sorted by the flag name. */ + const struct named_flag flags[] = { + { "authenticate-signed-writes", &chrc->props.IsSignedWritable }, + { "broadcast", &chrc->props.IsBroadcastable }, + { "extended-properties", &chrc->props.HasExtendedProperties }, + { "indicate", &chrc->props.IsIndicatable }, + { "notify", &chrc->props.IsNotifiable }, + { "read", &chrc->props.IsReadable }, + { "write", &chrc->props.IsWritable }, + { "write-without-response", &chrc->props.IsWritableWithoutResponse }, + }; + DBusMessageIter flags_iter; + + p_dbus_message_iter_recurse( variant, &flags_iter ); + while (p_dbus_message_iter_get_arg_type( &flags_iter ) != DBUS_TYPE_INVALID) + { + struct named_flag name, *flag; + + p_dbus_message_iter_get_basic( &flags_iter, &name.name ); + if ((flag = bsearch( &name, flags, ARRAY_SIZE( flags ), sizeof( *flags ), named_flag_cmp ))) + *flag->flag = TRUE; + else + FIXME( "Unknown characteristic flag: %s\n", debugstr_a( name.name ) ); + 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; + struct unix_name *service_name; + p_dbus_message_iter_get_basic( variant, &path ); + service_name = unix_name_get_or_create( path ); + chrc->service.handle = (UINT_PTR)service_name; + } + 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, &chrc->props.CharacteristicUuid ); + else + ERR( "Failed to parse UUID %s\n", debugstr_a( uuid_str ) ); + } + else if (!strcmp( prop_name, "Handle" ) + && p_dbus_message_iter_get_arg_type( variant ) == DBUS_TYPE_UINT16) + p_dbus_message_iter_get_basic( variant, &chrc->props.AttributeHandle ); +} + static NTSTATUS bluez_adapter_get_props_async( void *connection, const char *radio_object_path, DBusPendingCall **call ) { @@ -1695,6 +1769,43 @@ static void bluez_signal_handler( DBusConnection *conn, DBusMessage *msg, const unix_name_free( (struct unix_name *)event.gatt_service_added.device.handle ); } } + else if (!strcmp( iface_name, BLUEZ_INTERFACE_GATT_CHARACTERISTICS )) + { + struct winebluetooth_watcher_event_gatt_characteristic_added chrc_added = {0}; + union winebluetooth_watcher_event_data event = {0}; + struct unix_name *chrc_name; + DBusMessageIter props_iter, variant; + const char *prop_name; + + chrc_name = unix_name_get_or_create( object_path ); + chrc_added.characteristic.handle = (UINT_PTR)chrc_name; + if (!chrc_name) + { + ERR( "Failed to allocate memory for GATT characteristic path %s\n", debugstr_a( object_path ) ); + break; + } + p_dbus_message_iter_next( &iface_entry ); + p_dbus_message_iter_recurse( &iface_entry, &props_iter ); + + while ((prop_name = bluez_next_dict_entry( &props_iter, &variant ))) + bluez_gatt_characteristic_props_from_dict_entry( prop_name, &variant, &chrc_added ); + if (!chrc_added.service.handle) + { + unix_name_free( chrc_name ); + ERR( "Could not find the associated service for the GATT characteristic %s\n", + debugstr_a( object_path ) ); + break; + } + event.gatt_characteristic_added = chrc_added; + TRACE( "New BlueZ org.bluez.GattCharacterisic1 object added at %s: %p\n", debugstr_a( object_path ), + object_path ); + if (!bluez_event_list_queue_new_event( event_list, BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_ADDED, + event )) + { + unix_name_free( chrc_name ); + unix_name_free( (struct unix_name *)chrc_added.service.handle ); + } + } p_dbus_message_iter_next( &ifaces_iter ); } } @@ -2284,9 +2395,8 @@ static NTSTATUS bluez_build_initial_device_lists( DBusMessage *reply, struct lis } else if (!strcmp( iface, BLUEZ_INTERFACE_GATT_CHARACTERISTICS )) { - struct unix_name *service_name = NULL, *char_name; + struct unix_name *char_name; struct bluez_init_entry *init_entry; - BTH_LE_GATT_CHARACTERISTIC *props; DBusMessageIter variant; const char *prop_name; @@ -2304,76 +2414,17 @@ static NTSTATUS bluez_build_initial_device_lists( DBusMessage *reply, struct lis 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 ), debugstr_a( 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) + bluez_gatt_characteristic_props_from_dict_entry( prop_name, &variant, + &init_entry->object.characteristic ); + if (!init_entry->object.characteristic.service.handle) { 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 ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10099
From: Vibhav Pant <vibhavp@gmail.com> Enable GUID_BLUETOOTH_GATT_SERVICE_DEVICE_INTERFACE for service devices, and set address and service GUID devices properties as well. --- dlls/winebth.sys/winebth.c | 235 ++++++++++++++++++++++++++++++++----- include/bthledef.h | 1 + include/ddk/bthguid.h | 2 + 3 files changed, 206 insertions(+), 32 deletions(-) diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index fb2d9006d31..2650ed9f393 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -111,11 +111,15 @@ struct bluetooth_remote_device struct bluetooth_gatt_service { struct list entry; + BOOL removed; + DEVICE_OBJECT *device_obj; + struct bluetooth_remote_device *remote_device; /* The remote device this service exists on. */ winebluetooth_gatt_service_t service; GUID uuid; unsigned int primary : 1; UINT16 handle; + UNICODE_STRING service_symlink_name; CRITICAL_SECTION chars_cs; struct list characteristics; /* Guarded by chars_cs */ @@ -133,6 +137,7 @@ enum bluetooth_pdo_ext_type { BLUETOOTH_PDO_EXT_RADIO, BLUETOOTH_PDO_EXT_REMOTE_DEVICE, + BLUETOOTH_PDO_EXT_GATT_SERVICE, }; struct bluetooth_pdo_ext @@ -141,6 +146,7 @@ struct bluetooth_pdo_ext union { struct bluetooth_radio radio; struct bluetooth_remote_device remote_device; + struct bluetooth_gatt_service gatt_service; }; }; @@ -189,6 +195,17 @@ static struct bluetooth_gatt_service *find_gatt_service( struct list *services, return NULL; } +static NTSTATUS bluetooth_gatt_service_dispatch( DEVICE_OBJECT *device, struct bluetooth_gatt_service *ext, IRP *irp ) +{ + IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp ); + ULONG code = stack->Parameters.DeviceIoControl.IoControlCode; + NTSTATUS status = irp->IoStatus.Status; + + FIXME( "device=%p, ext=%p, irp=%p, code=%#lx: stub!\n", device, ext, irp, code ); + IoCompleteRequest( irp, IO_NO_INCREMENT ); + return status; +} + static NTSTATUS bluetooth_remote_device_dispatch( DEVICE_OBJECT *device, struct bluetooth_remote_device *ext, IRP *irp ) { IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp ); @@ -622,6 +639,8 @@ static NTSTATUS WINAPI dispatch_bluetooth( DEVICE_OBJECT *device, IRP *irp ) return bluetooth_radio_dispatch( device, &ext->radio, irp ); case BLUETOOTH_PDO_EXT_REMOTE_DEVICE: return bluetooth_remote_device_dispatch( device, &ext->remote_device, irp ); + case BLUETOOTH_PDO_EXT_GATT_SERVICE: + return bluetooth_gatt_service_dispatch( device, &ext->gatt_service, irp ); DEFAULT_UNREACHABLE; } } @@ -1163,36 +1182,47 @@ static void bluetooth_device_add_gatt_service( struct winebluetooth_watcher_even { if (winebluetooth_device_equal( event.device, device->device ) && !device->removed) { - struct bluetooth_gatt_service *service; + struct bluetooth_pdo_ext *ext; + DEVICE_OBJECT *device_obj; + NTSTATUS status; TRACE( "Adding GATT service %s for remote device %p\n", debugstr_guid( &event.uuid ), (void *)event.device.handle ); - service = calloc( 1, sizeof( *service ) ); - if (!service) + status = IoCreateDevice( driver_obj, sizeof( *ext ), NULL, FILE_DEVICE_BLUETOOTH, + FILE_AUTOGENERATED_DEVICE_NAME, FALSE, &device_obj ); + if (status) { - LeaveCriticalSection( &device_list_cs ); - return; + ERR( "Failed to create GATT service PDO, status %#lx\n", status ); + goto failed; } - service->service = event.service; - service->uuid = event.uuid; - service->primary = !!event.is_primary; - service->handle = event.attr_handle; + ext = device_obj->DeviceExtension; + ext->type = BLUETOOTH_PDO_EXT_GATT_SERVICE; + ext->gatt_service.device_obj = device_obj; + ext->gatt_service.service = event.service; + ext->gatt_service.uuid = event.uuid; + ext->gatt_service.primary = !!event.is_primary; + ext->gatt_service.handle = event.attr_handle; + ext->gatt_service.remote_device = device; + + list_init( &ext->gatt_service.characteristics ); + InitializeCriticalSectionEx( &ext->gatt_service.chars_cs, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO ); + ext->gatt_service.chars_cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": bluetooth_gatt_service.chars_cs"); 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_cs"); EnterCriticalSection( &device->props_cs ); - list_add_tail( &device->gatt_services, &service->entry ); + list_add_tail( &device->gatt_services, &ext->gatt_service.entry ); LeaveCriticalSection( &device->props_cs ); + LeaveCriticalSection( &device_list_cs ); winebluetooth_device_free( event.device ); + IoInvalidateDeviceRelations( device->device_obj, BusRelations ); return; } } } +failed: LeaveCriticalSection( &device_list_cs ); winebluetooth_device_free( event.device ); @@ -1222,20 +1252,11 @@ 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 ); + svc->removed = 1; LeaveCriticalSection( &device->props_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 ); + IoInvalidateDeviceRelations( device->device_obj, BusRelations ); winebluetooth_gatt_service_free( service ); return; } @@ -1507,6 +1528,46 @@ static NTSTATUS WINAPI fdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) return IoCallDriver( bus_pdo, irp ); } +static NTSTATUS gatt_service_query_id( struct bluetooth_gatt_service *ext, IRP *irp, BUS_QUERY_ID_TYPE type ) +{ + struct string_buffer buf = {0}; + + TRACE("(%p, %p, %s)\n", ext, irp, debugstr_BUS_QUERY_ID_TYPE( type ) ); + switch (type) + { + case BusQueryDeviceID: + append_id( &buf, L"WINEBTH\\GATTSVC" ); + break; + case BusQueryInstanceID: + { + BLUETOOTH_ADDRESS addr; + GUID uuid = ext->uuid; + + EnterCriticalSection( &ext->remote_device->props_cs ); + addr = ext->remote_device->props.address; + LeaveCriticalSection( &ext->remote_device->props_cs ); + append_id( &buf, L"%s&%02X%02X%02X%02X%02X%02X&{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}&%04X", + ext->remote_device->radio->hw_name, addr.rgBytes[0], addr.rgBytes[1], addr.rgBytes[2], + addr.rgBytes[3], addr.rgBytes[4], addr.rgBytes[5], uuid.Data1, uuid.Data2, uuid.Data3, uuid.Data4[0], + uuid.Data4[1], uuid.Data4[2], uuid.Data4[3], uuid.Data4[4], uuid.Data4[5], uuid.Data4[6], + uuid.Data4[7], ext->handle ); + break; + } + case BusQueryHardwareIDs: + case BusQueryCompatibleIDs: + append_id( &buf, L"" ); + break; + default: + return irp->IoStatus.Status; + } + + if (!buf.string) + return STATUS_NO_MEMORY; + + irp->IoStatus.Information = (ULONG_PTR)buf.string; + return STATUS_SUCCESS; +} + static NTSTATUS remote_device_query_id( struct bluetooth_remote_device *ext, IRP *irp, BUS_QUERY_ID_TYPE type ) { struct string_buffer buf = {0}; @@ -1617,10 +1678,94 @@ static void remove_pending_irps( struct bluetooth_radio *radio ) } } -static void remote_device_destroy( struct bluetooth_remote_device *ext ) +static NTSTATUS WINAPI gatt_service_pdo_pnp( DEVICE_OBJECT *device_obj, struct bluetooth_gatt_service *ext, IRP *irp ) { - struct bluetooth_gatt_service *svc, *next; + IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp ); + NTSTATUS ret = irp->IoStatus.Status; + + TRACE( "device_obj=%p, ext=%p, irp=%p, minor function=%s\n", device_obj, ext, irp, + debugstr_minor_function_code( stack->MinorFunction ) ); + switch (stack->MinorFunction) + { + case IRP_MN_QUERY_ID: + ret = gatt_service_query_id( ext, irp, stack->Parameters.QueryId.IdType ); + break; + case IRP_MN_QUERY_CAPABILITIES: + { + DEVICE_CAPABILITIES *caps = stack->Parameters.DeviceCapabilities.Capabilities; + caps->Removable = TRUE; + caps->SurpriseRemovalOK = TRUE; + caps->RawDeviceOK = TRUE; + ret = STATUS_SUCCESS; + break; + } + case IRP_MN_START_DEVICE: + { + WCHAR addr_str[13]; + BLUETOOTH_ADDRESS addr; + + EnterCriticalSection( &ext->remote_device->props_cs ); + addr = ext->remote_device->props.address; + LeaveCriticalSection( &ext->remote_device->props_cs ); + if (!IoRegisterDeviceInterface( device_obj, &GUID_BLUETOOTH_GATT_SERVICE_DEVICE_INTERFACE, NULL, + &ext->service_symlink_name )) + IoSetDeviceInterfaceState( &ext->service_symlink_name, TRUE ); + 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 ); + IoSetDevicePropertyData( device_obj, &DEVPKEY_Bluetooth_ServiceGUID, LOCALE_NEUTRAL, 0, DEVPROP_TYPE_GUID, + sizeof( ext->uuid ), &ext->uuid ); + ret = STATUS_SUCCESS; + break; + } + case IRP_MN_REMOVE_DEVICE: + { + struct bluetooth_gatt_characteristic *chrc, *next; + assert( ext->removed ); + if (ext->service_symlink_name.Buffer) + { + IoSetDeviceInterfaceState( &ext->service_symlink_name, FALSE ); + RtlFreeUnicodeString( &ext->service_symlink_name ); + } + winebluetooth_gatt_service_free( ext->service ); + ext->chars_cs.DebugInfo->Spare[0] = 0; + DeleteCriticalSection( &ext->chars_cs ); + LIST_FOR_EACH_ENTRY_SAFE( chrc, next, &ext->characteristics, struct bluetooth_gatt_characteristic, entry ) + { + winebluetooth_gatt_characteristic_free( chrc->characteristic ); + free( chrc ); + } + IoDeleteDevice( ext->device_obj ); + break; + } + case IRP_MN_SURPRISE_REMOVAL: + { + EnterCriticalSection( &ext->remote_device->props_cs ); + if (!ext->removed) + { + ext->removed = 1; + list_remove( &ext->entry ); + } + LeaveCriticalSection( &ext->remote_device->props_cs ); + ret = STATUS_SUCCESS; + break; + } + case IRP_MN_QUERY_DEVICE_TEXT: + WARN("Unhandled IRP_MN_QUERY_DEVICE_TEXT text type %u.\n", stack->Parameters.QueryDeviceText.DeviceTextType); + break; + default: + FIXME("Unhandled minor function %#x.\n", stack->MinorFunction ); + } + + irp->IoStatus.Status = ret; + IoCompleteRequest( irp, IO_NO_INCREMENT ); + return ret; +} + +static void remote_device_destroy( struct bluetooth_remote_device *ext ) +{ if (ext->bthle_symlink_name.Buffer) { IoSetDeviceInterfaceState( &ext->bthle_symlink_name, FALSE ); @@ -1629,12 +1774,6 @@ static void remote_device_destroy( struct bluetooth_remote_device *ext ) ext->props_cs.DebugInfo->Spare[0] = 0; DeleteCriticalSection( &ext->props_cs ); winebluetooth_device_free( ext->device ); - LIST_FOR_EACH_ENTRY_SAFE( svc, next, &ext->gatt_services, struct bluetooth_gatt_service, entry ) - { - winebluetooth_gatt_service_free( svc->service ); - list_remove( &svc->entry ); - free( svc ); - } IoDeleteDevice( ext->device_obj ); } @@ -1648,6 +1787,36 @@ static NTSTATUS WINAPI remote_device_pdo_pnp( DEVICE_OBJECT *device_obj, struct switch (stack->MinorFunction) { + case IRP_MN_QUERY_DEVICE_RELATIONS: + { + struct bluetooth_gatt_service *service; + DEVICE_RELATIONS *devices; + SIZE_T i = 0; + + if (stack->Parameters.QueryDeviceRelations.Type != BusRelations) + { + FIXME( "Unhandled Device Relation %x\n", stack->Parameters.QueryDeviceRelations.Type ); + break; + } + EnterCriticalSection( &ext->props_cs ); + devices = ExAllocatePool( PagedPool, offsetof( DEVICE_RELATIONS, Objects[list_count( &ext->gatt_services )] ) ); + if (!devices) + { + LeaveCriticalSection( &ext->props_cs ); + irp->IoStatus.Status = STATUS_NO_MEMORY; + break; + } + LIST_FOR_EACH_ENTRY( service, &ext->gatt_services, struct bluetooth_gatt_service, entry ) + { + devices->Objects[i++] = service->device_obj; + call_fastcall_func1( ObfReferenceObject, service->device_obj ); + } + LeaveCriticalSection( &ext->props_cs ); + devices->Count = i; + irp->IoStatus.Information = (ULONG_PTR)devices; + ret = STATUS_SUCCESS; + break; + } case IRP_MN_QUERY_ID: ret = remote_device_query_id( ext, irp, stack->Parameters.QueryId.IdType ); break; @@ -1869,6 +2038,8 @@ static NTSTATUS WINAPI bluetooth_pnp( DEVICE_OBJECT *device, IRP *irp ) return radio_pdo_pnp( device, &ext->radio, irp ); case BLUETOOTH_PDO_EXT_REMOTE_DEVICE: return remote_device_pdo_pnp( device, &ext->remote_device, irp ); + case BLUETOOTH_PDO_EXT_GATT_SERVICE: + return gatt_service_pdo_pnp( device, &ext->gatt_service, irp ); DEFAULT_UNREACHABLE; } } diff --git a/include/bthledef.h b/include/bthledef.h index 25bb2055db2..b4d9323c4da 100644 --- a/include/bthledef.h +++ b/include/bthledef.h @@ -51,5 +51,6 @@ typedef struct _BTH_LE_GATT_CHARACTERISTIC } 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( GUID_BLUETOOTH_GATT_SERVICE_DEVICE_INTERFACE, 0x6e3bb679, 0x4372, 0x40c8, 0x9e, 0xaa, 0x45, 0x09, 0xdf, 0x26, 0x0c, 0xd8 ); DEFINE_GUID( BTH_LE_ATT_BLUETOOTH_BASE_GUID, 0, 0, 0x1000, 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb ); #endif diff --git a/include/ddk/bthguid.h b/include/ddk/bthguid.h index 514747f92b5..2b850969972 100644 --- a/include/ddk/bthguid.h +++ b/include/ddk/bthguid.h @@ -25,5 +25,7 @@ DEFINE_DEVPROPKEY( DEVPKEY_Bluetooth_DeviceAddress, 0x2bd67d8b,0x8beb,0x48d5,0x8 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 ); +/* DEVPROP_TYPE_GUID */ +DEFINE_DEVPROPKEY( DEVPKEY_Bluetooth_ServiceGUID, 0x2bd67d8b,0x8beb,0x48d5,0x87,0xe0,0x6c,0xda,0x34,0x28,0x04,0x0a,2 ); #endif -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10099
From: Vibhav Pant <vibhavp@gmail.com> --- dlls/winebth.sys/winebth.c | 77 +++++++++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 22 deletions(-) diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index 2650ed9f393..37834bff89e 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -195,13 +195,66 @@ static struct bluetooth_gatt_service *find_gatt_service( struct list *services, return NULL; } +static NTSTATUS bluetooth_gatt_service_get_characteristics( struct bluetooth_gatt_service *service, IRP *irp ) +{ + 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; + IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp ); + ULONG outsize = stack->Parameters.DeviceIoControl.OutputBufferLength; + struct bluetooth_gatt_characteristic *chrc; + NTSTATUS status; + SIZE_T rem; + + if (outsize < min_size) + return STATUS_INVALID_USER_BUFFER; + + rem = (outsize - min_size)/sizeof( *chars->characteristics ); + status = STATUS_SUCCESS; + chars->count = 0; + + EnterCriticalSection( &service->chars_cs ); + LIST_FOR_EACH_ENTRY( chrc, &service->characteristics, struct bluetooth_gatt_characteristic, entry ) + { + chars->count++; + if (rem > 0) + { + chars->characteristics[chars->count - 1] = chrc->props; + rem--; + } + } + LeaveCriticalSection( &service->chars_cs ); + + irp->IoStatus.Information = offsetof( struct winebth_le_device_get_gatt_characteristics_params, characteristics[chars->count] ); + if (chars->count > rem) + status = STATUS_MORE_ENTRIES; + return status; +} + static NTSTATUS bluetooth_gatt_service_dispatch( DEVICE_OBJECT *device, struct bluetooth_gatt_service *ext, IRP *irp ) { IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp ); ULONG code = stack->Parameters.DeviceIoControl.IoControlCode; NTSTATUS status = irp->IoStatus.Status; - FIXME( "device=%p, ext=%p, irp=%p, code=%#lx: stub!\n", device, ext, irp, code ); + TRACE( "device=%p, ext=%p, irp=%p, code=%#lx\n", device, ext, irp, code ); + switch (code) + { + case IOCTL_WINEBTH_LE_DEVICE_GET_GATT_CHARACTERISTICS: + { + struct winebth_le_device_get_gatt_characteristics_params *params = irp->AssociatedIrp.SystemBuffer; + + if (!params) + { + status = STATUS_INVALID_USER_BUFFER; + break; + } + + status = bluetooth_gatt_service_get_characteristics( ext, irp ); + break; + } + default: + FIXME( "Unimplemented IOCTL code: %#lx\n", code ); + } IoCompleteRequest( irp, IO_NO_INCREMENT ); return status; } @@ -262,9 +315,7 @@ static NTSTATUS bluetooth_remote_device_dispatch( DEVICE_OBJECT *device, struct { 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) @@ -273,11 +324,7 @@ static NTSTATUS bluetooth_remote_device_dispatch( DEVICE_OBJECT *device, struct 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) @@ -287,22 +334,8 @@ static NTSTATUS bluetooth_remote_device_dispatch( DEVICE_OBJECT *device, struct 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 ); + status = bluetooth_gatt_service_get_characteristics( service, irp ); 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: -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10099
From: Vibhav Pant <vibhavp@gmail.com> --- dlls/bluetoothapis/tests/gatt.c | 159 ++++++++++++++++++++++++++++++-- include/bthledef.h | 32 +++++++ 2 files changed, 182 insertions(+), 9 deletions(-) diff --git a/dlls/bluetoothapis/tests/gatt.c b/dlls/bluetoothapis/tests/gatt.c index 89604f589dd..dec81a5a014 100644 --- a/dlls/bluetoothapis/tests/gatt.c +++ b/dlls/bluetoothapis/tests/gatt.c @@ -37,7 +37,18 @@ #include <wine/test.h> -static void test_for_all_le_devices( int line, void (*func)( HANDLE, void * ), void *data ) +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; +} + +static void test_for_all_le_devices( int line, void (*func)( HANDLE, const WCHAR *, void * ), void *data ) { char buffer[sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W) + MAX_PATH * sizeof( WCHAR )]; SP_DEVICE_INTERFACE_DETAIL_DATA_W *iface_detail = (SP_DEVICE_INTERFACE_DETAIL_DATA_W *)buffer; @@ -75,7 +86,7 @@ static void test_for_all_le_devices( int line, void (*func)( HANDLE, void * ), v FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); winetest_push_context( "device %lu", n++ ); trace( "Address: %s\n", debugstr_w( addr_str ) ); - func( device, data ); + func( device, addr_str, data ); winetest_pop_context(); CloseHandle( device ); found = TRUE; @@ -107,7 +118,69 @@ static const char *debugstr_BTH_LE_GATT_CHARACTERISTIC( const BTH_LE_GATT_CHARAC chrc->IsIndicatable, chrc->HasExtendedProperties ); } -static void test_service_BluetoothGATTGetCharacteristics( HANDLE device, BTH_LE_GATT_SERVICE *service ) +static void test_service_BluetoothGATTGetCharacteristics( HANDLE service, const BTH_LE_GATT_CHARACTERISTIC *chars, USHORT len ) +{ + HRESULT ret; + USHORT actual = 0, i, match = 0; + BTH_LE_GATT_CHARACTERISTIC *chars2; + + ret = BluetoothGATTGetCharacteristics( service, NULL, 0, NULL, &actual, 0 ); + todo_wine ok( ret == HRESULT_FROM_WIN32( ERROR_MORE_DATA ), "got ret %#lx\n", ret ); + if (!actual) + return; + + actual = len; + chars2 = calloc( actual, sizeof( *chars2 ) ); + ret = BluetoothGATTGetCharacteristics( service, NULL, actual, chars2, &actual, 0 ); + ok( ret == S_OK, "BluetoothGATTGetCharacteristics failed: %#lx\n", ret ); + ok( actual == len, "%u != %u\n", actual, len ); + + /* Characteristics retrieved using the device and GATT service handle should match. */ + for (i = 0; i < actual; i++) + { + const BTH_LE_GATT_CHARACTERISTIC *chrc1 = &chars[i]; + USHORT j; + + winetest_push_context( "chars[%u]", i ); + for (j = 0; j < actual; j++) + { + const BTH_LE_GATT_CHARACTERISTIC *chrc2 = &chars2[j]; + + if (IsBthLEUuidMatch( chrc1->CharacteristicUuid, chrc2->CharacteristicUuid ) + && chrc1->ServiceHandle == chrc2->ServiceHandle + && chrc1->AttributeHandle == chrc2->AttributeHandle) + { + winetest_push_context( "chars2[%u]", j ); + match++; + ok( chrc1->ServiceHandle == chrc2->ServiceHandle, "%#x != %#x\n", chrc1->ServiceHandle, + chrc2->ServiceHandle ); + ok( chrc1->AttributeHandle == chrc2->AttributeHandle, "%#x != %#x\n", chrc1->AttributeHandle, + chrc2->AttributeHandle ); + ok( chrc1->CharacteristicValueHandle == chrc2->CharacteristicValueHandle, "%#x != %#x\n", + chrc1->CharacteristicValueHandle, chrc2->CharacteristicValueHandle ); + +#define TEST_BOOL(field) ok( chrc1->field == chrc2->field, "%d != %d\n", chrc1->field, chrc2->field ) + TEST_BOOL(IsBroadcastable); + TEST_BOOL(IsReadable); + TEST_BOOL(IsWritable); + TEST_BOOL(IsWritableWithoutResponse); + TEST_BOOL(IsSignedWritable); + TEST_BOOL(IsNotifiable); + TEST_BOOL(IsIndicatable); + TEST_BOOL(HasExtendedProperties); +#undef TEST_BOOL + winetest_pop_context(); + break; + } + } + winetest_pop_context(); + } + + ok( actual == match, "%u != %u\n", actual, match ); + free( chars2 ); +} + +static void test_BluetoothGATTGetCharacteristics( HANDLE device, HANDLE service, BTH_LE_GATT_SERVICE *service_info ) { HRESULT ret; USHORT actual = 0, actual2 = 0, i; @@ -119,24 +192,83 @@ static void test_service_BluetoothGATTGetCharacteristics( HANDLE device, BTH_LE_ ret = BluetoothGATTGetCharacteristics( device, NULL, 0, NULL, &actual, 0 ); ok( ret == E_INVALIDARG, "got ret %#lx\n", ret ); - ret = BluetoothGATTGetCharacteristics( device, service, 0, NULL, &actual, 0 ); + ret = BluetoothGATTGetCharacteristics( device, service_info, 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 ); + ret = BluetoothGATTGetCharacteristics( device, service_info, 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 ); + ret = BluetoothGATTGetCharacteristics( device, service_info, 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] ) ); + if (service) + test_service_BluetoothGATTGetCharacteristics( service, buf, actual ); + else + skip( "Could not obtain handle to GATT service.\n" ); free( buf ); } -static void test_device_BluetoothGATTGetServices( HANDLE device, void *param ) +static HANDLE get_gatt_service_iface( const WCHAR *device_addr_str, const GUID *service_uuid ) +{ + char buffer[sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W) + MAX_PATH * sizeof( WCHAR )]; + SP_DEVICE_INTERFACE_DETAIL_DATA_W *iface_detail = (SP_DEVICE_INTERFACE_DETAIL_DATA_W *)buffer; + HANDLE device = INVALID_HANDLE_VALUE; + SP_DEVICE_INTERFACE_DATA iface_data; + DWORD idx = 0, ret; + HDEVINFO devinfo; + + devinfo = SetupDiGetClassDevsW( &GUID_BLUETOOTH_GATT_SERVICE_DEVICE_INTERFACE, NULL, NULL, + DIGCF_PRESENT | DIGCF_DEVICEINTERFACE ); + ret = GetLastError(); + ok( devinfo != INVALID_HANDLE_VALUE, "SetupDiGetClassDevsW failed: %lu\n", ret ); + if (devinfo == INVALID_HANDLE_VALUE) + return NULL; + + iface_detail->cbSize = sizeof( *iface_detail ); + iface_data.cbSize = sizeof( iface_data ); + while (SetupDiEnumDeviceInterfaces( devinfo, NULL, &GUID_BLUETOOTH_GATT_SERVICE_DEVICE_INTERFACE, idx++, + &iface_data )) + { + SP_DEVINFO_DATA devinfo_data = {0}; + WCHAR addr_str[13]; + DEVPROPTYPE type; + GUID uuid = {0}; + BOOL success; + + devinfo_data.cbSize = sizeof( devinfo_data ); + success = SetupDiGetDeviceInterfaceDetailW( devinfo, &iface_data, iface_detail, sizeof( buffer ), NULL, + &devinfo_data ); + ok( success, "SetupDiGetDeviceInterfaceDetailW failed: %lu\n", GetLastError() ); + addr_str[0] = 0; + success = SetupDiGetDevicePropertyW( devinfo, &devinfo_data, &DEVPKEY_Bluetooth_DeviceAddress, &type, (BYTE *)addr_str, + sizeof( addr_str ), NULL, 0 ); + ok( success, "SetupDiGetDevicePropertyW failed: %lu\n", GetLastError() ); + ok( type == DEVPROP_TYPE_STRING, "got type %#lx\n", type ); + if (wcsicmp( addr_str, device_addr_str )) + continue; + success = SetupDiGetDevicePropertyW( devinfo, &devinfo_data, &DEVPKEY_Bluetooth_ServiceGUID, &type, + (BYTE *)&uuid, sizeof( uuid ), NULL, 0 ); + ok( success, "SetupDiGetDevicePropertyW failed: %lu\n", GetLastError() ); + ok( type == DEVPROP_TYPE_GUID, "got type %#lx\n", type ); + if (!IsEqualGUID( &uuid, service_uuid )) + continue; + + device = CreateFileW( iface_detail->DevicePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ); + ret = GetLastError(); + break; + } + SetupDiDestroyDeviceInfoList( devinfo ); + ok( device != INVALID_HANDLE_VALUE || broken( ret == ERROR_GEN_FAILURE ), + "Could not find device interface for GATT service %s (error %lu) \n", debugstr_guid( service_uuid ), ret ); + return device == INVALID_HANDLE_VALUE ? NULL : device; +} + +static void test_device_BluetoothGATTGetServices( HANDLE device, const WCHAR *device_addr_str, void *param ) { HRESULT ret; USHORT actual = 0, i; @@ -176,9 +308,18 @@ static void test_device_BluetoothGATTGetServices( HANDLE device, void *param ) for (i = 0; i < actual; i++) { + HANDLE service; + GUID uuid; + winetest_push_context( "service %u", i ); trace( "%s\n", debugstr_BTH_LE_GATT_SERVICE( &buf[i] ) ); - test_service_BluetoothGATTGetCharacteristics( device, &buf[i] ); + + le_to_uuid( &buf[i].ServiceUuid, &uuid ); + service = get_gatt_service_iface( device_addr_str, &uuid ); + test_BluetoothGATTGetCharacteristics( device, service, &buf[i] ); + if (service) + CloseHandle( service ); + winetest_pop_context(); } @@ -195,7 +336,7 @@ static void test_BluetoothGATTGetServices( void ) test_for_all_le_devices( __LINE__, test_device_BluetoothGATTGetServices, NULL ); } -static void test_device_BluetoothGATTGetCharacteristics( HANDLE device, void *data ) +static void test_device_BluetoothGATTGetCharacteristics( HANDLE device, const WCHAR *addr, void *data ) { HRESULT ret; USHORT actual = 0; diff --git a/include/bthledef.h b/include/bthledef.h index b4d9323c4da..32585c06f8e 100644 --- a/include/bthledef.h +++ b/include/bthledef.h @@ -18,6 +18,10 @@ #ifndef __BTHLEDEFS_H #define __BTHLEDEFS_H +#ifdef __cplusplus +extern "C" { +#endif + typedef struct _BTH_LE_UUID { BOOLEAN IsShortUuid; @@ -53,4 +57,32 @@ typedef struct _BTH_LE_GATT_CHARACTERISTIC DEFINE_GUID( GUID_BLUETOOTHLE_DEVICE_INTERFACE, 0x781aee18, 0x7733, 0x4ce4, 0xad, 0xd0, 0x91, 0xf4, 0x1c, 0x67, 0xb5, 0x92 ); DEFINE_GUID( GUID_BLUETOOTH_GATT_SERVICE_DEVICE_INTERFACE, 0x6e3bb679, 0x4372, 0x40c8, 0x9e, 0xaa, 0x45, 0x09, 0xdf, 0x26, 0x0c, 0xd8 ); DEFINE_GUID( BTH_LE_ATT_BLUETOOTH_BASE_GUID, 0, 0, 0x1000, 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb ); + +FORCEINLINE BOOLEAN IsBthLEUuidMatch( BTH_LE_UUID u1, BTH_LE_UUID u2 ) +{ + GUID full1, full2; + + if (u1.IsShortUuid && u2.IsShortUuid) + return u1.Value.ShortUuid == u2.Value.ShortUuid; + + if (u1.IsShortUuid) + { + full1 = BTH_LE_ATT_BLUETOOTH_BASE_GUID; + full1.Data1 = u1.Value.ShortUuid; + } + else + full1 = u1.Value.LongUuid; + if (u2.IsShortUuid) + { + full2 = BTH_LE_ATT_BLUETOOTH_BASE_GUID; + full2.Data1 = u2.Value.ShortUuid; + } + else + full2 = u2.Value.LongUuid; + return !memcmp( &full1, &full2, sizeof( full1 ) ); +} + +#ifdef __cplusplus +} +#endif #endif -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10099
From: Vibhav Pant <vibhavp@gmail.com> --- dlls/winebth.sys/dbus.c | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index 44098f1abd1..3dcd108c393 100644 --- a/dlls/winebth.sys/dbus.c +++ b/dlls/winebth.sys/dbus.c @@ -264,6 +264,29 @@ static const char *dbgstr_dbus_error( const DBusError *error ) return wine_dbg_sprintf( "{%s: %s}", debugstr_a( error->name ), debugstr_a( error->message ) ); } +static const char *dbgstr_dbus_iter( DBusMessageIter *iter ) +{ + int arg_type, next; + + if (!iter) return "(null)"; + + arg_type = p_dbus_message_iter_get_arg_type( iter ); + if (arg_type == DBUS_TYPE_INVALID) + return wine_dbg_sprintf( "{%p arg=DBUS_TYPE_INVALID}", iter ); + + next = p_dbus_message_iter_has_next( iter ); + if (arg_type == DBUS_TYPE_ARRAY) + { + int elem_type, count; + + elem_type = p_dbus_message_iter_get_element_type( iter ); + count = p_dbus_message_iter_get_element_count( iter ); + return wine_dbg_sprintf( "{%p arg=%c elem=%c count=%d next=%d}", iter, arg_type, elem_type, + count, next ); + } + return wine_dbg_sprintf( "{%p arg=%c next=%d}", iter, arg_type, next ); +} + static NTSTATUS bluez_get_objects_async( DBusConnection *connection, DBusPendingCall **call ) { DBusMessage *request; @@ -638,7 +661,7 @@ static void bluez_radio_prop_from_dict_entry( const char *prop_name, DBusMessage winebluetooth_radio_props_mask_t *props_mask, winebluetooth_radio_props_mask_t wanted_props_mask ) { - TRACE_(dbus)( "(%s, %p, %p, %p, %#x)\n", debugstr_a( prop_name ), variant, props, props_mask, + TRACE_(dbus)( "(%s, %s, %p, %p, %#x)\n", debugstr_a( prop_name ), dbgstr_dbus_iter( variant ), props, props_mask, wanted_props_mask ); if (wanted_props_mask & WINEBLUETOOTH_RADIO_PROPERTY_NAME && @@ -730,7 +753,7 @@ static void bluez_device_prop_from_dict_entry( const char *prop_name, DBusMessag winebluetooth_device_props_mask_t *props_mask, winebluetooth_device_props_mask_t wanted_props_mask ) { - TRACE_( dbus )( "(%s, %p, %p, %p, %#x)\n", debugstr_a( prop_name ), variant, props, props_mask, + TRACE_( dbus )( "(%s, %s, %p, %p, %#x)\n", debugstr_a( prop_name ), dbgstr_dbus_iter( variant ), props, props_mask, wanted_props_mask ); @@ -809,7 +832,7 @@ static void bluez_device_prop_from_dict_entry( const char *prop_name, DBusMessag static void bluez_gatt_service_props_from_dict_entry( const char *prop_name, DBusMessageIter *variant, struct winebluetooth_watcher_event_gatt_service_added *service ) { - TRACE_( dbus )( "(%s, %p, %p)\n", debugstr_a( prop_name ), variant, service ); + TRACE_( dbus )( "(%s, %s, %p)\n", debugstr_a( prop_name ), dbgstr_dbus_iter( variant ), service ); if (!strcmp( prop_name, "Device" ) && p_dbus_message_iter_get_arg_type( variant ) == DBUS_TYPE_OBJECT_PATH ) @@ -857,7 +880,7 @@ static void bluez_gatt_characteristic_props_from_dict_entry( const char *prop_name, DBusMessageIter *variant, struct winebluetooth_watcher_event_gatt_characteristic_added *chrc ) { - TRACE_( dbus )( "(%s, %p, %p)\n", debugstr_a( prop_name ), variant, chrc ); + TRACE_( dbus )( "(%s, %s, %p)\n", debugstr_a( prop_name ), dbgstr_dbus_iter( variant ), chrc ); if (!strcmp( prop_name, "Flags" ) && p_dbus_message_iter_get_arg_type ( variant ) == DBUS_TYPE_ARRAY -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10099
From: Vibhav Pant <vibhavp@gmail.com> --- dlls/bluetoothapis/gatt.c | 21 +++++++++++---------- dlls/bluetoothapis/tests/gatt.c | 2 +- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/dlls/bluetoothapis/gatt.c b/dlls/bluetoothapis/gatt.c index 3432faf321b..38cd8a1694e 100644 --- a/dlls/bluetoothapis/gatt.c +++ b/dlls/bluetoothapis/gatt.c @@ -111,16 +111,17 @@ HRESULT WINAPI BluetoothGATTGetCharacteristics( HANDLE device, BTH_LE_GATT_SERVI 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) + if (buf && !count) + 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 ); + if (service) + 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() ); diff --git a/dlls/bluetoothapis/tests/gatt.c b/dlls/bluetoothapis/tests/gatt.c index dec81a5a014..d442ab21f67 100644 --- a/dlls/bluetoothapis/tests/gatt.c +++ b/dlls/bluetoothapis/tests/gatt.c @@ -125,7 +125,7 @@ static void test_service_BluetoothGATTGetCharacteristics( HANDLE service, const BTH_LE_GATT_CHARACTERISTIC *chars2; ret = BluetoothGATTGetCharacteristics( service, NULL, 0, NULL, &actual, 0 ); - todo_wine ok( ret == HRESULT_FROM_WIN32( ERROR_MORE_DATA ), "got ret %#lx\n", ret ); + ok( ret == HRESULT_FROM_WIN32( ERROR_MORE_DATA ), "got ret %#lx\n", ret ); if (!actual) return; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10099
4/7 is missing setting the IOSB status from bluetooth_gatt_service_dispatch(). -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10099#note_129905
participants (3)
-
Elizabeth Figura (@zfigura) -
Vibhav Pant -
Vibhav Pant (@vibhavp)