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