This MR is the first of multiples MRs adding support for the Bluetooth stack API in Wine:
1. The winebth.sys driver, which talks to BlueZ and implements several key IOCTLs for communicating with Bluetooth devices and radios. 2. A bthserv service, which is responsible for keeping track of the authentication agent, and relaying authentication requests and responses to and from the driver. 3. Userspace APIs (bluetoothapis.dll, bthprops.cpl, Windows.Devices.Bluetooth, etc).
winebth.sys is split into two "sub" drivers:
`winebth.sys`: The main entrypoint, loaded by winedevice. It listens for changes to Bluetooth devices and radios and authentication events on BlueZ, passing them on the bthenum. It also handles most IOCTL operations on Bluetooth radio PDOs.
~~`bthenum`: Responsible for creating nodes for discovered Bluetooth devices and associated services. It also tries to validate any IOCTLs relating to bluetooth devices before passing them to winebth.sys. (Not in this MR, likely in part 2)~~
The unix code is split between dbus.c, unixlib.c and winebluetooth.c, where winebluetooth is a simple wrapper around unixlib for the sake of organization.
-- v34: dlls/winebth.sys: Register and enable BTHPORT_DEVICE and BLUETOOTH_RADIO interfaces for radio PDOs. dlls/winebth.sys: Derive a unique hardware ID for radio PDOs from their corresponding BlueZ object path. dlls/winebth.sys: Create radio PDOs from the list of org.bluez.Adapter1 objects on BlueZ. dlls/winebth.sys: Add a basic unixlib stub using DBus. dlls/winebth.sys: Add base winebth.sys driver.
From: Vibhav Pant vibhavp@gmail.com
--- MAINTAINERS | 5 ++ configure | 2 + configure.ac | 1 + dlls/winebth.sys/Makefile.in | 8 ++ dlls/winebth.sys/winebth.c | 149 ++++++++++++++++++++++++++++++++ dlls/winebth.sys/winebth.inf | 22 +++++ dlls/winebth.sys/winebth.rc | 20 +++++ dlls/winebth.sys/winebth_priv.h | 74 ++++++++++++++++ loader/wine.inf.in | 1 + programs/wineboot/wineboot.c | 1 + 10 files changed, 283 insertions(+) create mode 100644 dlls/winebth.sys/Makefile.in create mode 100644 dlls/winebth.sys/winebth.c create mode 100644 dlls/winebth.sys/winebth.inf create mode 100644 dlls/winebth.sys/winebth.rc create mode 100644 dlls/winebth.sys/winebth_priv.h
diff --git a/MAINTAINERS b/MAINTAINERS index 6d22ae52d49..c50c5d235c9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -49,6 +49,11 @@ F: dlls/x3daudio*/ F: dlls/xapofx*/ F: dlls/xaudio*/
+Bluetooth support +M: Vibhav Pant vibhavp@gmail.com +F: dlls/winebth.sys/ +F: dlls/bluetoothapis/sdp.c + Common Controls Library P: Nikolay Sivov nsivov@codeweavers.com P: Zhiyi Zhang zzhang@codeweavers.com diff --git a/configure b/configure index 983eb81d27f..932307d024d 100755 --- a/configure +++ b/configure @@ -1511,6 +1511,7 @@ enable_windowscodecs enable_windowscodecsext enable_winealsa_drv enable_wineandroid_drv +enable_winebth_sys enable_winebus_sys enable_winecoreaudio_drv enable_winecrt0 @@ -22854,6 +22855,7 @@ wine_fn_config_makefile dlls/windowscodecsext enable_windowscodecsext wine_fn_config_makefile dlls/windowscodecsext/tests enable_tests wine_fn_config_makefile dlls/winealsa.drv enable_winealsa_drv wine_fn_config_makefile dlls/wineandroid.drv enable_wineandroid_drv +wine_fn_config_makefile dlls/winebth.sys enable_winebth_sys wine_fn_config_makefile dlls/winebus.sys enable_winebus_sys wine_fn_config_makefile dlls/winecoreaudio.drv enable_winecoreaudio_drv wine_fn_config_makefile dlls/winecrt0 enable_winecrt0 diff --git a/configure.ac b/configure.ac index 94260d7ae7b..4e3311d877d 100644 --- a/configure.ac +++ b/configure.ac @@ -3252,6 +3252,7 @@ WINE_CONFIG_MAKEFILE(dlls/windowscodecsext) WINE_CONFIG_MAKEFILE(dlls/windowscodecsext/tests) WINE_CONFIG_MAKEFILE(dlls/winealsa.drv) WINE_CONFIG_MAKEFILE(dlls/wineandroid.drv) +WINE_CONFIG_MAKEFILE(dlls/winebth.sys) WINE_CONFIG_MAKEFILE(dlls/winebus.sys) WINE_CONFIG_MAKEFILE(dlls/winecoreaudio.drv) WINE_CONFIG_MAKEFILE(dlls/winecrt0) diff --git a/dlls/winebth.sys/Makefile.in b/dlls/winebth.sys/Makefile.in new file mode 100644 index 00000000000..19f754cb487 --- /dev/null +++ b/dlls/winebth.sys/Makefile.in @@ -0,0 +1,8 @@ +MODULE = winebth.sys +IMPORTS = ntoskrnl + +EXTRADLLFLAGS = -Wl,--subsystem,native + +SOURCES = \ + winebth.c \ + winebth.rc diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c new file mode 100644 index 00000000000..27808483e02 --- /dev/null +++ b/dlls/winebth.sys/winebth.c @@ -0,0 +1,149 @@ +/* + * Bluetooth bus driver + * + * Copyright 2024 Vibhav Pant + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <stdarg.h> +#include <stdlib.h> +#include <assert.h> + +#include <ntstatus.h> +#define WIN32_NO_STATUS +#include <windef.h> +#include <winbase.h> +#include <winternl.h> +#include <winioctl.h> +#include <ddk/wdm.h> + +#include <wine/debug.h> +#include <wine/list.h> + +#include "winebth_priv.h" + +WINE_DEFAULT_DEBUG_CHANNEL( winebth ); + +static DRIVER_OBJECT *driver_obj; + +static DEVICE_OBJECT *bus_fdo, *bus_pdo; + +static NTSTATUS WINAPI fdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) +{ + IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp ); + + TRACE( "irp %p, minor function %s.\n", irp, debugstr_minor_function_code( stack->MinorFunction ) ); + + switch (stack->MinorFunction) + { + case IRP_MN_START_DEVICE: + irp->IoStatus.Status = STATUS_SUCCESS; + break; + case IRP_MN_SURPRISE_REMOVAL: + irp->IoStatus.Status = STATUS_SUCCESS; + break; + case IRP_MN_REMOVE_DEVICE: + { + NTSTATUS ret; + IoSkipCurrentIrpStackLocation( irp ); + ret = IoCallDriver( bus_pdo, irp ); + IoDetachDevice( bus_pdo ); + IoDeleteDevice( bus_fdo ); + return ret; + } + default: + FIXME( "Unhandled minor function %s.\n", debugstr_minor_function_code( stack->MinorFunction ) ); + } + + IoSkipCurrentIrpStackLocation( irp ); + return IoCallDriver( bus_pdo, irp ); +} + +static NTSTATUS WINAPI pdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) +{ + IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp); + NTSTATUS ret = irp->IoStatus.Status; + + TRACE( "device_obj %p, irp %p, minor function %s\n", device_obj, irp, debugstr_minor_function_code( stack->MinorFunction ) ); + switch (stack->MinorFunction) + { + 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: + ret = STATUS_SUCCESS; + break; + case IRP_MN_REMOVE_DEVICE: + IoDeleteDevice( device_obj ); + case IRP_MN_SURPRISE_REMOVAL: + irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest( irp, IO_NO_INCREMENT ); + return STATUS_SUCCESS; + default: + FIXME( "Unhandled minor function %s.\n", debugstr_minor_function_code( stack->MinorFunction ) ); + break; + } + + irp->IoStatus.Status = ret; + IoCompleteRequest( irp, IO_NO_INCREMENT ); + return ret; +} + +static NTSTATUS WINAPI bluetooth_pnp( DEVICE_OBJECT *device, IRP *irp ) +{ + if (device == bus_fdo) + return fdo_pnp( device, irp ); + return pdo_pnp( device, irp ); +} + +static NTSTATUS WINAPI driver_add_device( DRIVER_OBJECT *driver, DEVICE_OBJECT *pdo ) +{ + NTSTATUS ret; + + TRACE( "(%p, %p)\n", driver, pdo ); + ret = IoCreateDevice( driver, 0, NULL, FILE_DEVICE_BUS_EXTENDER, 0, FALSE, &bus_fdo ); + if (ret != STATUS_SUCCESS) + { + ERR( "failed to create FDO: %#lx\n", ret ); + return ret; + } + + IoAttachDeviceToDeviceStack( bus_fdo, pdo ); + bus_pdo = pdo; + bus_fdo->Flags &= ~DO_DEVICE_INITIALIZING; + return STATUS_SUCCESS; +} + +static void WINAPI driver_unload( DRIVER_OBJECT *driver ) {} + +NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) +{ + TRACE( "(%p, %s)\n", driver, debugstr_w( path->Buffer ) ); + + driver->DriverExtension->AddDevice = driver_add_device; + driver_obj = driver; + + driver->MajorFunction[IRP_MJ_PNP] = bluetooth_pnp; + driver->DriverUnload = driver_unload; + + return STATUS_SUCCESS; +} diff --git a/dlls/winebth.sys/winebth.inf b/dlls/winebth.sys/winebth.inf new file mode 100644 index 00000000000..6b3b8be0ed0 --- /dev/null +++ b/dlls/winebth.sys/winebth.inf @@ -0,0 +1,22 @@ +[Version] +Signature="$CHICAGO$" +ClassGuid={4d36e97d-e325-11ce-bfc1-08002be10318} +Class=System + +[Manufacturer] +Wine=mfg_section + +[mfg_section] +Wine Bluetooth bus driver=device_section,root\winebth + +[device_section.Services] +AddService = winebth,0x2,svc_section + +[svc_section] +Description="Wine Bluetooth bus driver" +DisplayName="Wine Bluetooth bus" +ServiceBinary="%12%\winebth.sys" +LoadOrderGroup="WinePlugPlay" +ServiceType=1 +StartType=3 +ErrorControl=1 diff --git a/dlls/winebth.sys/winebth.rc b/dlls/winebth.sys/winebth.rc new file mode 100644 index 00000000000..33157042b7c --- /dev/null +++ b/dlls/winebth.sys/winebth.rc @@ -0,0 +1,20 @@ +/* + * Copyright 2024 Vibhav Pant + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* @makedep: winebth.inf */ +1 WINE_DATA_FILE winebth.inf diff --git a/dlls/winebth.sys/winebth_priv.h b/dlls/winebth.sys/winebth_priv.h new file mode 100644 index 00000000000..25d69dd2386 --- /dev/null +++ b/dlls/winebth.sys/winebth_priv.h @@ -0,0 +1,74 @@ +/* + * Private winebth.sys defs + * + * Copyright 2024 Vibhav Pant + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __WINE_WINEBTH_WINEBTH_H_ +#define __WINE_WINEBTH_WINEBTH_H_ + +#ifdef __ASM_USE_FASTCALL_WRAPPER +extern void * WINAPI wrap_fastcall_func1(void *func, const void *a); +__ASM_STDCALL_FUNC(wrap_fastcall_func1, 8, + "popl %ecx\n\t" + "popl %eax\n\t" + "xchgl (%esp),%ecx\n\t" + "jmp *%eax"); +#define call_fastcall_func1(func,a) wrap_fastcall_func1(func,a) +#else +#define call_fastcall_func1(func,a) func(a) +#endif + +#define XX(i) case (i): return #i + + +static inline const char *debugstr_minor_function_code( UCHAR code ) +{ + switch(code) + { + XX(IRP_MN_START_DEVICE); + XX(IRP_MN_QUERY_REMOVE_DEVICE); + XX(IRP_MN_REMOVE_DEVICE); + XX(IRP_MN_CANCEL_REMOVE_DEVICE); + XX(IRP_MN_STOP_DEVICE); + XX(IRP_MN_QUERY_STOP_DEVICE); + XX(IRP_MN_CANCEL_STOP_DEVICE); + XX(IRP_MN_QUERY_DEVICE_RELATIONS); + XX(IRP_MN_QUERY_INTERFACE); + XX(IRP_MN_QUERY_CAPABILITIES); + XX(IRP_MN_QUERY_RESOURCES); + XX(IRP_MN_QUERY_RESOURCE_REQUIREMENTS); + XX(IRP_MN_QUERY_DEVICE_TEXT); + XX(IRP_MN_FILTER_RESOURCE_REQUIREMENTS); + XX(IRP_MN_READ_CONFIG); + XX(IRP_MN_WRITE_CONFIG); + XX(IRP_MN_EJECT); + XX(IRP_MN_SET_LOCK); + XX(IRP_MN_QUERY_ID); + XX(IRP_MN_QUERY_PNP_DEVICE_STATE); + XX(IRP_MN_QUERY_BUS_INFORMATION); + XX(IRP_MN_DEVICE_USAGE_NOTIFICATION); + XX(IRP_MN_SURPRISE_REMOVAL); + XX(IRP_MN_QUERY_LEGACY_BUS_INFORMATION); + default: + return wine_dbg_sprintf( "(unknown %#x)", code ); + } +} +#undef XX + + +#endif /* __WINE_WINEBTH_WINEBTH_H_ */ diff --git a/loader/wine.inf.in b/loader/wine.inf.in index 181a6d62c9b..50fb2c828cb 100644 --- a/loader/wine.inf.in +++ b/loader/wine.inf.in @@ -2629,6 +2629,7 @@ services,"@%11%\ws2_32.dll,-4" [InfFiles] input.inf,"@%12%\hidclass.sys,-1" mouhid.inf,"@%12%\mouhid.sys,-1" +winebth.inf,"@%12%\winebth.sys,-1" winebus.inf,"@%12%\winebus.sys,-1" winehid.inf,"@%12%\winehid.sys,-1" wineusb.inf,"@%12%\wineusb.sys,-1" diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c index 20260f94369..318939d1daa 100644 --- a/programs/wineboot/wineboot.c +++ b/programs/wineboot/wineboot.c @@ -1544,6 +1544,7 @@ static void install_root_pnp_devices(void) } root_devices[] = { + {"root\wine\winebth", "root\winebth\0", "C:\windows\inf\winebth.inf"}, {"root\wine\winebus", "root\winebus\0", "C:\windows\inf\winebus.inf"}, {"root\wine\wineusb", "root\wineusb\0", "C:\windows\inf\wineusb.inf"}, };
From: Vibhav Pant vibhavp@gmail.com
--- dlls/winebth.sys/Makefile.in | 7 +- dlls/winebth.sys/dbus.c | 129 +++++++++++++++++++++++++++++++ dlls/winebth.sys/dbus.h | 113 +++++++++++++++++++++++++++ dlls/winebth.sys/unixlib.c | 62 +++++++++++++++ dlls/winebth.sys/unixlib.h | 38 +++++++++ dlls/winebth.sys/unixlib_priv.h | 38 +++++++++ dlls/winebth.sys/winebluetooth.c | 51 ++++++++++++ dlls/winebth.sys/winebth.c | 13 +++- dlls/winebth.sys/winebth_priv.h | 11 +++ 9 files changed, 458 insertions(+), 4 deletions(-) create mode 100644 dlls/winebth.sys/dbus.c create mode 100644 dlls/winebth.sys/dbus.h create mode 100644 dlls/winebth.sys/unixlib.c create mode 100644 dlls/winebth.sys/unixlib.h create mode 100644 dlls/winebth.sys/unixlib_priv.h create mode 100644 dlls/winebth.sys/winebluetooth.c
diff --git a/dlls/winebth.sys/Makefile.in b/dlls/winebth.sys/Makefile.in index 19f754cb487..12d1b4ddcc9 100644 --- a/dlls/winebth.sys/Makefile.in +++ b/dlls/winebth.sys/Makefile.in @@ -1,8 +1,13 @@ MODULE = winebth.sys -IMPORTS = ntoskrnl +IMPORTS = ntoskrnl uuid +UNIXLIB = winebth.so +UNIX_CFLAGS = $(DBUS_CFLAGS)
EXTRADLLFLAGS = -Wl,--subsystem,native
SOURCES = \ + dbus.c \ + unixlib.c \ + winebluetooth.c \ winebth.c \ winebth.rc diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c new file mode 100644 index 00000000000..7840bba161d --- /dev/null +++ b/dlls/winebth.sys/dbus.c @@ -0,0 +1,129 @@ +/* + * Support for communicating with BlueZ over DBus. + * + * Copyright 2024 Vibhav Pant + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + + +#if 0 +#pragma makedep unix +#endif + +#include "config.h" + +#include <stdlib.h> +#include <dlfcn.h> + +#ifdef SONAME_LIBDBUS_1 +#include <dbus/dbus.h> +#endif + +#include <ntstatus.h> +#define WIN32_NO_STATUS +#include <windef.h> +#include <winternl.h> +#include <winbase.h> + +#include <wine/debug.h> + +#include "unixlib.h" +#include "unixlib_priv.h" +#include "dbus.h" + +WINE_DEFAULT_DEBUG_CHANNEL( winebth ); +WINE_DECLARE_DEBUG_CHANNEL( dbus ); + +#ifdef SONAME_LIBDBUS_1 + +const int bluez_timeout = -1; + +#define BLUEZ_DEST "org.bluez" +#define BLUEZ_INTERFACE_ADAPTER "org.bluez.Adapter1" + +#define DO_FUNC( f ) typeof( f ) (*p_##f) +DBUS_FUNCS; +#undef DO_FUNC + +static BOOL load_dbus_functions( void ) +{ + void *handle = dlopen( SONAME_LIBDBUS_1, RTLD_NOW ); + + if (handle == NULL) goto failed; + +#define DO_FUNC( f ) \ + if (!( p_##f = dlsym( handle, #f ) )) \ + { \ + ERR( "failed to load symbol %s: %s\n", #f, dlerror() ); \ + goto failed; \ + } + DBUS_FUNCS; +#undef DO_FUNC + return TRUE; + +failed: + WARN( "failed to load DBus support: %s\n", dlerror() ); + return FALSE; +} + +static inline const char *dbgstr_dbus_connection( DBusConnection *connection ) +{ + return wine_dbg_sprintf( "{%p connected=%d}", connection, + p_dbus_connection_get_is_connected( connection ) ); +} + +void *bluez_dbus_init( void ) +{ + DBusError error; + DBusConnection *connection; + + if (!load_dbus_functions()) return NULL; + + p_dbus_threads_init_default(); + p_dbus_error_init ( &error ); + + connection = p_dbus_bus_get_private ( DBUS_BUS_SYSTEM, &error ); + if (!connection) + { + ERR( "Failed to get system dbus connection: %s: %s\n", debugstr_a( error.name ), debugstr_a( error.message ) ); + p_dbus_error_free( &error ); + return NULL; + } + + return connection; +} + +void bluez_dbus_close( void *connection ) +{ + TRACE_(dbus)( "(%s)\n", dbgstr_dbus_connection( connection ) ); + + p_dbus_connection_flush( connection ); + p_dbus_connection_close( connection ); +} + +void bluez_dbus_free( void *connection ) +{ + TRACE_(dbus)( "(%s)\n", dbgstr_dbus_connection( connection ) ); + + p_dbus_connection_unref( connection ); +} +#else + +void *bluez_dbus_init( void ) { return NULL; } +void bluez_dbus_close( void *connection ) {} +void bluez_dbus_free( void *connection ) {} + +#endif /* SONAME_LIBDBUS_1 */ diff --git a/dlls/winebth.sys/dbus.h b/dlls/winebth.sys/dbus.h new file mode 100644 index 00000000000..025d2c6fd2c --- /dev/null +++ b/dlls/winebth.sys/dbus.h @@ -0,0 +1,113 @@ +/* + * DBus declarations. + * + * Copyright 2024 Vibhav Pant + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __WINE_BLUETOOTHAPIS_UNIXLIB_DBUS_H +#define __WINE_BLUETOOTHAPIS_UNIXLIB_DBUS_H + +#include "config.h" + +#ifdef SONAME_LIBDBUS_1 +#include <dbus/dbus.h> +#endif + +#ifdef SONAME_LIBDBUS_1 + +#define DBUS_FUNCS \ + DO_FUNC(dbus_bus_add_match); \ + DO_FUNC(dbus_bus_get); \ + DO_FUNC(dbus_bus_get_id); \ + DO_FUNC(dbus_bus_get_private); \ + DO_FUNC(dbus_bus_remove_match); \ + DO_FUNC(dbus_connection_add_filter); \ + DO_FUNC(dbus_connection_close); \ + DO_FUNC(dbus_connection_flush); \ + DO_FUNC(dbus_connection_free_preallocated_send); \ + DO_FUNC(dbus_connection_get_is_anonymous); \ + DO_FUNC(dbus_connection_get_is_authenticated); \ + DO_FUNC(dbus_connection_get_is_connected); \ + DO_FUNC(dbus_connection_get_server_id); \ + DO_FUNC(dbus_connection_get_unix_process_id); \ + DO_FUNC(dbus_connection_get_unix_fd); \ + DO_FUNC(dbus_connection_get_unix_user); \ + DO_FUNC(dbus_connection_preallocate_send); \ + DO_FUNC(dbus_connection_read_write_dispatch); \ + DO_FUNC(dbus_connection_remove_filter); \ + DO_FUNC(dbus_connection_ref); \ + DO_FUNC(dbus_connection_send); \ + DO_FUNC(dbus_connection_send_preallocated); \ + DO_FUNC(dbus_connection_send_with_reply); \ + DO_FUNC(dbus_connection_send_with_reply_and_block); \ + DO_FUNC(dbus_connection_try_register_object_path); \ + DO_FUNC(dbus_connection_unref); \ + DO_FUNC(dbus_connection_unregister_object_path); \ + DO_FUNC(dbus_error_free); \ + DO_FUNC(dbus_error_has_name) ; \ + DO_FUNC(dbus_error_init); \ + DO_FUNC(dbus_error_is_set); \ + DO_FUNC(dbus_free); \ + DO_FUNC(dbus_free_string_array); \ + DO_FUNC(dbus_message_append_args); \ + DO_FUNC(dbus_message_get_args); \ + DO_FUNC(dbus_message_iter_get_element_count); \ + DO_FUNC(dbus_message_get_interface); \ + DO_FUNC(dbus_message_get_member); \ + DO_FUNC(dbus_message_get_path); \ + DO_FUNC(dbus_message_get_sender); \ + DO_FUNC(dbus_message_get_serial); \ + DO_FUNC(dbus_message_get_signature); \ + DO_FUNC(dbus_message_get_type); \ + DO_FUNC(dbus_message_has_signature); \ + DO_FUNC(dbus_message_iter_has_next); \ + DO_FUNC(dbus_message_is_error); \ + DO_FUNC(dbus_message_is_method_call); \ + DO_FUNC(dbus_message_is_signal); \ + DO_FUNC(dbus_message_iter_append_basic); \ + DO_FUNC(dbus_message_iter_close_container); \ + DO_FUNC(dbus_message_iter_get_arg_type); \ + DO_FUNC(dbus_message_iter_get_element_type); \ + DO_FUNC(dbus_message_iter_get_basic); \ + DO_FUNC(dbus_message_iter_get_fixed_array); \ + DO_FUNC(dbus_message_iter_get_signature); \ + DO_FUNC(dbus_message_iter_init); \ + DO_FUNC(dbus_message_iter_init_append); \ + DO_FUNC(dbus_message_iter_next); \ + DO_FUNC(dbus_message_iter_open_container); \ + DO_FUNC(dbus_message_iter_recurse); \ + DO_FUNC(dbus_message_new_error); \ + DO_FUNC(dbus_message_new_error_printf); \ + DO_FUNC(dbus_message_new_method_return); \ + DO_FUNC(dbus_message_new_method_call); \ + DO_FUNC(dbus_message_ref); \ + DO_FUNC(dbus_message_unref); \ + DO_FUNC(dbus_pending_call_block); \ + DO_FUNC(dbus_pending_call_cancel); \ + DO_FUNC(dbus_pending_call_get_completed); \ + DO_FUNC(dbus_pending_call_set_notify); \ + DO_FUNC(dbus_pending_call_steal_reply); \ + DO_FUNC(dbus_pending_call_unref); \ + DO_FUNC(dbus_set_error_from_message); \ + DO_FUNC(dbus_threads_init_default); + +#define DO_FUNC( f ) extern typeof( f ) *p_##f +DBUS_FUNCS; +#undef DO_FUNC + +#endif /* SONAME_LIBDBUS_1 */ +#endif /* __WINE_BLUETOOTHAPIS_UNIXLIB_DBUS_H */ diff --git a/dlls/winebth.sys/unixlib.c b/dlls/winebth.sys/unixlib.c new file mode 100644 index 00000000000..c289a690659 --- /dev/null +++ b/dlls/winebth.sys/unixlib.c @@ -0,0 +1,62 @@ +/* + * winebluetooth Unix interface + * + * Copyright 2024 Vibhav Pant + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#if 0 +#pragma makedep unix +#endif + +#include <config.h> + +#include <stdarg.h> + +#include <ntstatus.h> +#define WIN32_NO_STATUS +#include <wine/debug.h> + +#include "unixlib.h" +#include "unixlib_priv.h" + +WINE_DEFAULT_DEBUG_CHANNEL( winebth ); + +static void *dbus_connection; + +static NTSTATUS bluetooth_init ( void *params ) +{ + dbus_connection = bluez_dbus_init(); + TRACE("dbus_connection=%p\n", dbus_connection); + + return dbus_connection ? STATUS_SUCCESS : STATUS_INTERNAL_ERROR; +} + +static NTSTATUS bluetooth_shutdown( void *params ) +{ + if (!dbus_connection) return STATUS_NOT_SUPPORTED; + + bluez_dbus_close( dbus_connection ); + bluez_dbus_free( dbus_connection ); + return STATUS_SUCCESS; +} + +const unixlib_entry_t __wine_unix_call_funcs[] = { + bluetooth_init, + bluetooth_shutdown, +}; + +C_ASSERT( ARRAYSIZE( __wine_unix_call_funcs ) == unix_funcs_count ); diff --git a/dlls/winebth.sys/unixlib.h b/dlls/winebth.sys/unixlib.h new file mode 100644 index 00000000000..e79a2c4745a --- /dev/null +++ b/dlls/winebth.sys/unixlib.h @@ -0,0 +1,38 @@ +/* + * Unix interface definitions + * + * Copyright 2024 Vibhav Pant + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __WINE_WINEBTH_UNIXLIB_H +#define __WINE_WINEBTH_UNIXLIB_H + +#include <stdarg.h> + +#include <wine/unixlib.h> + +enum bluetoothapis_funcs +{ + unix_bluetooth_init, + unix_bluetooth_shutdown, + + unix_funcs_count +}; + +#define UNIX_BLUETOOTH_CALL( func, params ) WINE_UNIX_CALL( unix_##func, params ) + +#endif /* __WINE_WINEBTH_UNIXLIB_H */ diff --git a/dlls/winebth.sys/unixlib_priv.h b/dlls/winebth.sys/unixlib_priv.h new file mode 100644 index 00000000000..e1215f3590a --- /dev/null +++ b/dlls/winebth.sys/unixlib_priv.h @@ -0,0 +1,38 @@ +/* + * Bluetoothapis Unix interface + * + * Copyright 2024 Vibhav Pant + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __WINE_WINEBTH_UNIXLIB_PRIV_H +#define __WINE_WINEBTH_UNIXLIB_PRIV_H + +#include <config.h> + +#include <ntstatus.h> +#define WIN32_NO_STATUS +#include <windef.h> + +#include <wine/list.h> +#include <wine/rbtree.h> + +#include "unixlib.h" + +extern void *bluez_dbus_init( void ); +extern void bluez_dbus_close( void *connection ); +extern void bluez_dbus_free( void *connection ); +#endif /* __WINE_WINEBTH_UNIXLIB_PRIV_H */ diff --git a/dlls/winebth.sys/winebluetooth.c b/dlls/winebth.sys/winebluetooth.c new file mode 100644 index 00000000000..0895311d569 --- /dev/null +++ b/dlls/winebth.sys/winebluetooth.c @@ -0,0 +1,51 @@ +/* + * Wine bluetooth APIs + * + * Copyright 2024 Vibhav Pant + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + */ + +#include <stdarg.h> + +#include <ntstatus.h> +#define WIN32_NO_STATUS + +#include <windef.h> +#include <winbase.h> + +#include <wine/debug.h> +#include <wine/heap.h> +#include <wine/unixlib.h> + +#include "winebth_priv.h" +#include "unixlib.h" + +NTSTATUS winebluetooth_init( void ) +{ + NTSTATUS status; + + status = __wine_init_unix_call(); + if (status != STATUS_SUCCESS) + return status; + + return UNIX_BLUETOOTH_CALL( bluetooth_init, NULL ); +} + +NTSTATUS winebluetooth_shutdown( void ) +{ + return UNIX_BLUETOOTH_CALL( bluetooth_shutdown, NULL ); +} diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index 27808483e02..afcd39466e9 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -27,6 +27,8 @@ #include <windef.h> #include <winbase.h> #include <winternl.h> +#include <initguid.h> +#include <devpkey.h> #include <winioctl.h> #include <ddk/wdm.h>
@@ -58,6 +60,7 @@ static NTSTATUS WINAPI fdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) case IRP_MN_REMOVE_DEVICE: { NTSTATUS ret; + winebluetooth_shutdown(); IoSkipCurrentIrpStackLocation( irp ); ret = IoCallDriver( bus_pdo, irp ); IoDetachDevice( bus_pdo ); @@ -137,13 +140,17 @@ static void WINAPI driver_unload( DRIVER_OBJECT *driver ) {}
NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) { + NTSTATUS status; TRACE( "(%p, %s)\n", driver, debugstr_w( path->Buffer ) );
- driver->DriverExtension->AddDevice = driver_add_device; + status = winebluetooth_init(); + if (status != STATUS_SUCCESS) + return status; + driver_obj = driver;
- driver->MajorFunction[IRP_MJ_PNP] = bluetooth_pnp; + driver->DriverExtension->AddDevice = driver_add_device; driver->DriverUnload = driver_unload; - + driver->MajorFunction[IRP_MJ_PNP] = bluetooth_pnp; return STATUS_SUCCESS; } diff --git a/dlls/winebth.sys/winebth_priv.h b/dlls/winebth.sys/winebth_priv.h index 25d69dd2386..ca38aea44ac 100644 --- a/dlls/winebth.sys/winebth_priv.h +++ b/dlls/winebth.sys/winebth_priv.h @@ -21,6 +21,10 @@ #ifndef __WINE_WINEBTH_WINEBTH_H_ #define __WINE_WINEBTH_WINEBTH_H_
+#include <ddk/wdm.h> + +#include <wine/debug.h> + #ifdef __ASM_USE_FASTCALL_WRAPPER extern void * WINAPI wrap_fastcall_func1(void *func, const void *a); __ASM_STDCALL_FUNC(wrap_fastcall_func1, 8, @@ -32,6 +36,11 @@ __ASM_STDCALL_FUNC(wrap_fastcall_func1, 8, #else #define call_fastcall_func1(func,a) func(a) #endif +struct string_buffer +{ + WCHAR *string; + size_t len; +};
#define XX(i) case (i): return #i
@@ -70,5 +79,7 @@ static inline const char *debugstr_minor_function_code( UCHAR code ) } #undef XX
+NTSTATUS winebluetooth_init( void ); +NTSTATUS winebluetooth_shutdown( void );
#endif /* __WINE_WINEBTH_WINEBTH_H_ */
From: Vibhav Pant vibhavp@gmail.com
Add a "bluetooth watcher", which builds a list of radio devices discovered from BlueZ and sends them to the driver in the form of BLUETOOTH_WATCHER_RADIO_DEVICE_ADDED events. Initially, send a list of org.bluez.Adapter1 objects discovered via GetManagedObjects. --- dlls/winebth.sys/dbus.c | 238 +++++++++++++++++++++++++++++++ dlls/winebth.sys/unixlib.c | 88 +++++++++++- dlls/winebth.sys/unixlib.h | 32 +++++ dlls/winebth.sys/unixlib_priv.h | 13 ++ dlls/winebth.sys/winebluetooth.c | 23 +++ dlls/winebth.sys/winebth.c | 220 +++++++++++++++++++++++++++- dlls/winebth.sys/winebth_priv.h | 58 ++++++++ 7 files changed, 665 insertions(+), 7 deletions(-)
diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index 7840bba161d..01789088ed6 100644 --- a/dlls/winebth.sys/dbus.c +++ b/dlls/winebth.sys/dbus.c @@ -27,6 +27,8 @@
#include <stdlib.h> #include <dlfcn.h> +#include <assert.h> +#include <pthread.h>
#ifdef SONAME_LIBDBUS_1 #include <dbus/dbus.h> @@ -37,9 +39,13 @@ #include <windef.h> #include <winternl.h> #include <winbase.h> +#include <bthsdpdef.h> +#include <bluetoothapis.h>
#include <wine/debug.h>
+#include "winebth_priv.h" + #include "unixlib.h" #include "unixlib_priv.h" #include "dbus.h" @@ -51,6 +57,8 @@ WINE_DECLARE_DEBUG_CHANNEL( dbus );
const int bluez_timeout = -1;
+#define DBUS_INTERFACE_OBJECTMANAGER "org.freedesktop.DBus.ObjectManager" + #define BLUEZ_DEST "org.bluez" #define BLUEZ_INTERFACE_ADAPTER "org.bluez.Adapter1"
@@ -79,12 +87,75 @@ failed: return FALSE; }
+static const char *bluez_next_dict_entry( DBusMessageIter *iter, DBusMessageIter *variant ) +{ + DBusMessageIter sub; + const char *name; + + if (p_dbus_message_iter_get_arg_type( iter ) != DBUS_TYPE_DICT_ENTRY) + return NULL; + + p_dbus_message_iter_recurse( iter, &sub ); + p_dbus_message_iter_next( iter ); + p_dbus_message_iter_get_basic( &sub, &name ); + p_dbus_message_iter_next( &sub ); + p_dbus_message_iter_recurse( &sub, variant ); + return name; +} + static inline const char *dbgstr_dbus_connection( DBusConnection *connection ) { return wine_dbg_sprintf( "{%p connected=%d}", connection, p_dbus_connection_get_is_connected( connection ) ); }
+static NTSTATUS bluez_get_objects_async( DBusConnection *connection, DBusPendingCall **call ) +{ + DBusMessage *request; + dbus_bool_t success; + + TRACE( "Getting managed objects under '/' at service '%s'\n", BLUEZ_DEST ); + request = p_dbus_message_new_method_call( + BLUEZ_DEST, "/", DBUS_INTERFACE_OBJECTMANAGER, "GetManagedObjects" ); + if (!request) + { + return STATUS_NO_MEMORY; + } + + success = p_dbus_connection_send_with_reply( connection, request, call, -1 ); + p_dbus_message_unref( request ); + if (!success) + return STATUS_NO_MEMORY; + + if (*call == NULL) + return STATUS_INVALID_PARAMETER; + + return STATUS_SUCCESS; +} + +#define DBUS_OBJECTMANAGER_METHOD_GETMANAGEDOBJECTS_RETURN_SIGNATURE \ + DBUS_TYPE_ARRAY_AS_STRING \ + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING \ + DBUS_TYPE_OBJECT_PATH_AS_STRING \ + DBUS_TYPE_ARRAY_AS_STRING \ + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING \ + DBUS_TYPE_STRING_AS_STRING \ + DBUS_TYPE_ARRAY_AS_STRING \ + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING \ + DBUS_TYPE_STRING_AS_STRING \ + DBUS_TYPE_VARIANT_AS_STRING \ + DBUS_DICT_ENTRY_END_CHAR_AS_STRING \ + DBUS_DICT_ENTRY_END_CHAR_AS_STRING \ + DBUS_DICT_ENTRY_END_CHAR_AS_STRING + +struct bluez_watcher_ctx +{ + void *init_device_list_call; + + /* struct bluez_init_entry */ + struct list initial_radio_list; +}; + void *bluez_dbus_init( void ) { DBusError error; @@ -120,10 +191,177 @@ void bluez_dbus_free( void *connection )
p_dbus_connection_unref( connection ); } + +struct bluez_watcher_event +{ + struct list entry; + enum winebluetooth_watcher_event_type event_type; + union winebluetooth_watcher_event_data event; +}; + +NTSTATUS bluez_watcher_init( void *connection, void **ctx ) +{ + NTSTATUS status; + DBusPendingCall *call; + struct bluez_watcher_ctx *watcher_ctx = + calloc( 1, sizeof( struct bluez_watcher_ctx ) ); + + if (watcher_ctx == NULL) return STATUS_NO_MEMORY; + status = bluez_get_objects_async( connection, &call ); + if (status != STATUS_SUCCESS) + { + free( watcher_ctx ); + ERR( "could not create async GetManagedObjects call: %#x\n", (int)status); + return status; + } + watcher_ctx->init_device_list_call = call; + list_init( &watcher_ctx->initial_radio_list ); + + *ctx = watcher_ctx; + TRACE( "ctx=%p\n", ctx ); + return STATUS_SUCCESS; +} + +struct bluez_init_entry +{ + union { + struct winebluetooth_watcher_event_radio_added radio; + } object; + struct list entry; +}; + +static NTSTATUS bluez_build_initial_device_lists( DBusMessage *reply, struct list *adapter_list ) +{ + DBusMessageIter dict, paths_iter, iface_iter, prop_iter; + const char *path; + NTSTATUS status = STATUS_SUCCESS; + + if (!p_dbus_message_has_signature( reply, + DBUS_OBJECTMANAGER_METHOD_GETMANAGEDOBJECTS_RETURN_SIGNATURE )) + { + ERR( "Unexpected signature in GetManagedObjects reply: %s\n", + debugstr_a( p_dbus_message_get_signature( reply ) ) ); + return STATUS_INTERNAL_ERROR; + } + + p_dbus_message_iter_init( reply, &dict ); + p_dbus_message_iter_recurse( &dict, &paths_iter ); + while((path = bluez_next_dict_entry( &paths_iter, &iface_iter ))) + { + const char *iface; + while ((iface = bluez_next_dict_entry ( &iface_iter, &prop_iter ))) + { + if (!strcmp( iface, BLUEZ_INTERFACE_ADAPTER )) + { + struct bluez_init_entry *init_device = calloc( 1, sizeof( *init_device ) ); + struct unix_name *radio_name; + + if (!init_device) + { + status = STATUS_NO_MEMORY; + goto done; + } + radio_name = unix_name_get_or_create( path ); + if (!radio_name) + { + free( init_device ); + status = STATUS_NO_MEMORY; + goto done; + } + init_device->object.radio.radio.handle = (UINT_PTR)radio_name; + list_add_tail( adapter_list, &init_device->entry ); + TRACE( "Found BlueZ org.bluez.Adapter1 object %s: %p\n", + debugstr_a( radio_name->str ), radio_name ); + break; + } + } + } + + TRACE( "Initial device list: radios: %d\n", list_count( adapter_list ) ); + done: + return status; +} + +static struct bluez_init_entry *bluez_init_entries_list_pop( struct list *list ) +{ + struct list *entry = list_head( list ); + struct bluez_init_entry *device = LIST_ENTRY( entry, struct bluez_init_entry, entry ); + + list_remove( entry ); + return device; +} + +NTSTATUS bluez_dbus_loop( void *c, void *watcher, + struct winebluetooth_event *result ) +{ + DBusConnection *connection; + struct bluez_watcher_ctx *watcher_ctx = watcher; + + TRACE( "(%p, %p, %p)\n", c, watcher, result ); + connection = p_dbus_connection_ref( c ); + + while (TRUE) + { + if (!list_empty( &watcher_ctx->initial_radio_list )) + { + struct bluez_init_entry *radio = + bluez_init_entries_list_pop( &watcher_ctx->initial_radio_list ); + struct winebluetooth_watcher_event *watcher_event = &result->data.watcher_event; + + result->status = WINEBLUETOOTH_EVENT_WATCHER_EVENT; + watcher_event->event_type = BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED; + watcher_event->event_data.radio_added = radio->object.radio; + free( radio ); + p_dbus_connection_unref( connection ); + return STATUS_PENDING; + } + else if (!p_dbus_connection_read_write_dispatch( connection, 100 )) + { + p_dbus_connection_unref( connection ); + TRACE( "Disconnected from DBus\n" ); + return STATUS_SUCCESS; + } + + if (watcher_ctx->init_device_list_call != NULL + && p_dbus_pending_call_get_completed( watcher_ctx->init_device_list_call )) + { + DBusMessage *reply = p_dbus_pending_call_steal_reply( watcher_ctx->init_device_list_call ); + DBusError error; + NTSTATUS status; + + p_dbus_pending_call_unref( watcher_ctx->init_device_list_call ); + watcher_ctx->init_device_list_call = NULL; + + p_dbus_error_init( &error ); + if (p_dbus_set_error_from_message( &error, reply )) + { + ERR( "Error getting object list from BlueZ: '%s': '%s'\n", error.name, + error.message ); + p_dbus_error_free( &error ); + p_dbus_message_unref( reply ); + p_dbus_connection_unref( connection ); + return STATUS_NO_MEMORY; + } + status = bluez_build_initial_device_lists( reply, &watcher_ctx->initial_radio_list ); + p_dbus_message_unref( reply ); + if (status != STATUS_SUCCESS) + { + ERR( "Error building initial bluetooth devices list: %#x\n", (int)status ); + p_dbus_connection_unref( connection ); + return status; + } + } + } +} #else
void *bluez_dbus_init( void ) { return NULL; } void bluez_dbus_close( void *connection ) {} void bluez_dbus_free( void *connection ) {} +NTSTATUS bluez_watcher_init( void *connection, void **ctx ) { return STATUS_NOT_SUPPORTED; } +NTSTATUS bluez_dbus_loop( void *c, void *watcher, struct winebluetooth_event *result ) +{ + return STATUS_NOT_SUPPORTED; +}
#endif /* SONAME_LIBDBUS_1 */ diff --git a/dlls/winebth.sys/unixlib.c b/dlls/winebth.sys/unixlib.c index c289a690659..69f3493848f 100644 --- a/dlls/winebth.sys/unixlib.c +++ b/dlls/winebth.sys/unixlib.c @@ -24,10 +24,18 @@
#include <config.h>
+#include <stdlib.h> #include <stdarg.h> +#include <assert.h> +#include <pthread.h>
#include <ntstatus.h> #define WIN32_NO_STATUS +#include <winternl.h> +#include <winbase.h> +#include <windef.h> +#include <wine/list.h> +#include <wine/rbtree.h> #include <wine/debug.h>
#include "unixlib.h" @@ -35,14 +43,70 @@
WINE_DEFAULT_DEBUG_CHANNEL( winebth );
+static int compare_string( const void *key, const struct wine_rb_entry *entry ) +{ + struct unix_name *str = WINE_RB_ENTRY_VALUE( entry, struct unix_name, entry ); + return strcmp( key, str->str ); +} + +static struct rb_tree names = { .compare = compare_string }; +static pthread_mutex_t names_mutex = PTHREAD_MUTEX_INITIALIZER; + +struct unix_name *unix_name_get_or_create( const char *str ) +{ + struct rb_entry *entry; + struct unix_name *s; + + pthread_mutex_lock( &names_mutex ); + entry = rb_get( &names, str ); + if (!entry) + { + struct unix_name *s = malloc( sizeof( struct unix_name ) ); + if (!s) + { + pthread_mutex_unlock( &names_mutex ); + return NULL; + } + s->str = strdup( str ); + s->refcnt = 0; + rb_put( &names, str, &s->entry ); + entry = &s->entry; + } + s = RB_ENTRY_VALUE( entry, struct unix_name, entry ); + s->refcnt++; + pthread_mutex_unlock( &names_mutex ); + return s; +} + +void unix_name_free( struct unix_name *name ) +{ + pthread_mutex_lock( &names_mutex ); + name->refcnt--; + if (name->refcnt == 0) + { + rb_remove( &names, &name->entry ); + free( name ); + } + pthread_mutex_unlock( &names_mutex ); +} + static void *dbus_connection; +static void *bluetooth_watcher;
static NTSTATUS bluetooth_init ( void *params ) { + NTSTATUS status; + dbus_connection = bluez_dbus_init(); - TRACE("dbus_connection=%p\n", dbus_connection); + if (!dbus_connection) + return STATUS_INTERNAL_ERROR;
- return dbus_connection ? STATUS_SUCCESS : STATUS_INTERNAL_ERROR; + status = bluez_watcher_init( dbus_connection, &bluetooth_watcher ); + if (status) + bluez_dbus_close( dbus_connection ); + else + TRACE( "dbus_connection=%p bluetooth_watcher=%p\n", dbus_connection, bluetooth_watcher ); + return status; }
static NTSTATUS bluetooth_shutdown( void *params ) @@ -54,9 +118,29 @@ static NTSTATUS bluetooth_shutdown( void *params ) return STATUS_SUCCESS; }
+static NTSTATUS bluetooth_adapter_free( void *args ) +{ + struct bluetooth_adapter_free_params *params = args; + unix_name_free( params->adapter ); + return STATUS_SUCCESS; +} + +static NTSTATUS bluetooth_get_event( void *args ) +{ + struct bluetooth_get_event_params *params = args; + + if (!dbus_connection) return STATUS_NOT_SUPPORTED; + memset( ¶ms->result, 0, sizeof( params->result ) ); + return bluez_dbus_loop( dbus_connection, bluetooth_watcher, ¶ms->result ); +} + const unixlib_entry_t __wine_unix_call_funcs[] = { bluetooth_init, bluetooth_shutdown, + + bluetooth_adapter_free, + + bluetooth_get_event, };
C_ASSERT( ARRAYSIZE( __wine_unix_call_funcs ) == unix_funcs_count ); diff --git a/dlls/winebth.sys/unixlib.h b/dlls/winebth.sys/unixlib.h index e79a2c4745a..46509bfb20a 100644 --- a/dlls/winebth.sys/unixlib.h +++ b/dlls/winebth.sys/unixlib.h @@ -21,15 +21,47 @@ #ifndef __WINE_WINEBTH_UNIXLIB_H #define __WINE_WINEBTH_UNIXLIB_H
+#include <stdlib.h> #include <stdarg.h>
+#include <windef.h> +#include <winbase.h> + +#include <bthsdpdef.h> +#include <bluetoothapis.h> + #include <wine/unixlib.h> +#include <wine/debug.h> + +#include "winebth_priv.h" + +#ifdef WINE_UNIX_LIB +typedef struct unix_name *unix_name_t; +typedef void *unix_handle_t; +#else +typedef UINT_PTR unix_name_t; +typedef UINT_PTR unix_handle_t; +#endif + +struct bluetooth_adapter_free_params +{ + unix_name_t adapter; +}; + +struct bluetooth_get_event_params +{ + struct winebluetooth_event result; +};
enum bluetoothapis_funcs { unix_bluetooth_init, unix_bluetooth_shutdown,
+ unix_bluetooth_adapter_free, + + unix_bluetooth_get_event, + unix_funcs_count };
diff --git a/dlls/winebth.sys/unixlib_priv.h b/dlls/winebth.sys/unixlib_priv.h index e1215f3590a..f74bd6b2e8b 100644 --- a/dlls/winebth.sys/unixlib_priv.h +++ b/dlls/winebth.sys/unixlib_priv.h @@ -32,7 +32,20 @@
#include "unixlib.h"
+struct unix_name +{ + char *str; + SIZE_T refcnt; + + struct wine_rb_entry entry; +}; + +extern struct unix_name *unix_name_get_or_create( const char *str ); +extern void unix_name_free( struct unix_name *name ); + extern void *bluez_dbus_init( void ); extern void bluez_dbus_close( void *connection ); extern void bluez_dbus_free( void *connection ); +extern NTSTATUS bluez_dbus_loop( void *connection, void *watcher_ctx, struct winebluetooth_event *result ); +extern NTSTATUS bluez_watcher_init( void *connection, void **ctx ); #endif /* __WINE_WINEBTH_UNIXLIB_PRIV_H */ diff --git a/dlls/winebth.sys/winebluetooth.c b/dlls/winebth.sys/winebluetooth.c index 0895311d569..c0088577d13 100644 --- a/dlls/winebth.sys/winebluetooth.c +++ b/dlls/winebth.sys/winebluetooth.c @@ -34,6 +34,29 @@ #include "winebth_priv.h" #include "unixlib.h"
+WINE_DEFAULT_DEBUG_CHANNEL( winebth ); + +void winebluetooth_radio_free( winebluetooth_radio_t radio ) +{ + struct bluetooth_adapter_free_params args = { 0 }; + TRACE( "(%p)\n", (void *)radio.handle ); + + args.adapter = radio.handle; + UNIX_BLUETOOTH_CALL( bluetooth_adapter_free, &args ); +} + +NTSTATUS winebluetooth_get_event( struct winebluetooth_event *result ) +{ + struct bluetooth_get_event_params params = {0}; + NTSTATUS status; + + TRACE( "(%p)\n", result ); + + status = UNIX_BLUETOOTH_CALL( bluetooth_get_event, ¶ms ); + *result = params.result; + return status; +} + NTSTATUS winebluetooth_init( void ) { NTSTATUS status; diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index afcd39466e9..434604b4dad 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -43,6 +43,132 @@ static DRIVER_OBJECT *driver_obj;
static DEVICE_OBJECT *bus_fdo, *bus_pdo;
+#define DECLARE_CRITICAL_SECTION( cs ) \ + static CRITICAL_SECTION cs; \ + static CRITICAL_SECTION_DEBUG cs##_debug = { \ + 0, \ + 0, \ + &( cs ), \ + { &cs##_debug.ProcessLocksList, &cs##_debug.ProcessLocksList }, \ + 0, \ + 0, \ + { (DWORD_PTR)( __FILE__ ": " #cs ) } }; \ + static CRITICAL_SECTION cs = { &cs##_debug, -1, 0, 0, 0, 0 }; + +DECLARE_CRITICAL_SECTION( device_list_cs ); + +static struct list device_list = LIST_INIT( device_list ); + +struct bluetooth_radio +{ + struct list entry; + BOOL removed; + + DEVICE_OBJECT *device_obj; + winebluetooth_radio_t radio; +}; + +void WINAPIV append_id( struct string_buffer *buffer, const WCHAR *format, ... ) +{ + va_list args; + WCHAR *string; + int len; + + va_start( args, format ); + + len = _vsnwprintf( NULL, 0, format, args ) + 1; + if (!(string = ExAllocatePool( PagedPool, (buffer->len + len) * sizeof( WCHAR ) ))) + { + if (buffer->string) + ExFreePool( buffer->string ); + buffer->string = NULL; + return; + } + if (buffer->string) + { + memcpy( string, buffer->string, buffer->len * sizeof( WCHAR ) ); + ExFreePool( buffer->string ); + } + _vsnwprintf( string + buffer->len, len, format, args ); + buffer->string = string; + buffer->len += len; + + va_end( args ); +} + + +static HANDLE event_loop_thread; + +static void add_bluetooth_radio( struct winebluetooth_watcher_event_radio_added event ) +{ + struct bluetooth_radio *device; + DEVICE_OBJECT *device_obj; + UNICODE_STRING string; + NTSTATUS status; + WCHAR name[256]; + static unsigned int radio_index; + + swprintf( name, ARRAY_SIZE( name ), L"\Device\WINEBTH-RADIO-%d", radio_index++ ); + TRACE( "Adding new bluetooth radio %p: %s\n", (void *)event.radio.handle, debugstr_w( name ) ); + + RtlInitUnicodeString( &string, name ); + status = IoCreateDevice( driver_obj, sizeof( *device ), &string, FILE_DEVICE_BLUETOOTH, 0, + FALSE, &device_obj ); + if (status) + { + ERR( "Failed to create device, status %#lx\n", status ); + return; + } + + device = device_obj->DeviceExtension; + device->device_obj = device_obj; + device->radio = event.radio; + device->removed = FALSE; + + EnterCriticalSection( &device_list_cs ); + list_add_tail( &device_list, &device->entry ); + LeaveCriticalSection( &device_list_cs ); + + IoInvalidateDeviceRelations( bus_pdo, BusRelations ); +} + +static DWORD CALLBACK bluetooth_event_loop_thread_proc( void *arg ) +{ + NTSTATUS status; + while (TRUE) + { + struct winebluetooth_event result = {0}; + + status = winebluetooth_get_event( &result ); + if (status != STATUS_PENDING) break; + + switch (result.status) + { + case WINEBLUETOOTH_EVENT_WATCHER_EVENT: + { + struct winebluetooth_watcher_event *event = &result.data.watcher_event; + switch (event->event_type) + { + case BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED: + add_bluetooth_radio( event->event_data.radio_added ); + break; + default: + FIXME( "Unknown bluetooth watcher event code: %#x\n", event->event_type ); + } + break; + } + default: + FIXME( "Unknown bluetooth event loop status code: %#x\n", result.status ); + } + } + + if (status != STATUS_SUCCESS) + ERR( "Bluetooth event loop terminated with %#lx", status ); + else + TRACE( "Exiting bluetooth event loop\n" ); + return 0; +} + static NTSTATUS WINAPI fdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) { IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp ); @@ -51,7 +177,44 @@ static NTSTATUS WINAPI fdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp )
switch (stack->MinorFunction) { + case IRP_MN_QUERY_DEVICE_RELATIONS: + { + struct bluetooth_radio *radio; + 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( &device_list_cs ); + devices = ExAllocatePool( + PagedPool, offsetof( DEVICE_RELATIONS, Objects[list_count( &device_list )] ) ); + if (devices == NULL) + { + LeaveCriticalSection( &device_list_cs ); + irp->IoStatus.Status = STATUS_NO_MEMORY; + break; + } + + LIST_FOR_EACH_ENTRY(radio, &device_list, struct bluetooth_radio, entry) + { + devices->Objects[i++] = radio->device_obj; + call_fastcall_func1( ObfReferenceObject, radio->device_obj ); + } + LeaveCriticalSection( &device_list_cs ); + + devices->Count = i; + irp->IoStatus.Information = (ULONG_PTR)devices; + irp->IoStatus.Status = STATUS_SUCCESS; + break; + } case IRP_MN_START_DEVICE: + event_loop_thread = + CreateThread( NULL, 0, bluetooth_event_loop_thread_proc, NULL, 0, NULL ); irp->IoStatus.Status = STATUS_SUCCESS; break; case IRP_MN_SURPRISE_REMOVAL: @@ -59,8 +222,20 @@ static NTSTATUS WINAPI fdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) break; case IRP_MN_REMOVE_DEVICE: { + struct bluetooth_radio *device, *cur; NTSTATUS ret; winebluetooth_shutdown(); + WaitForSingleObject( event_loop_thread, INFINITE ); + CloseHandle( event_loop_thread ); + EnterCriticalSection( &device_list_cs ); + LIST_FOR_EACH_ENTRY_SAFE( device, cur, &device_list, struct bluetooth_radio, entry ) + { + assert( !device->removed ); + winebluetooth_radio_free( device->radio ); + list_remove( &device->entry ); + IoDeleteDevice( device->device_obj ); + } + LeaveCriticalSection( &device_list_cs ); IoSkipCurrentIrpStackLocation( irp ); ret = IoCallDriver( bus_pdo, irp ); IoDetachDevice( bus_pdo ); @@ -75,14 +250,39 @@ static NTSTATUS WINAPI fdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) return IoCallDriver( bus_pdo, irp ); }
+static NTSTATUS query_id(const struct bluetooth_radio *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 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 WINAPI pdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) { IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp); + struct bluetooth_radio *device = device_obj->DeviceExtension; NTSTATUS ret = irp->IoStatus.Status;
TRACE( "device_obj %p, irp %p, minor function %s\n", device_obj, irp, debugstr_minor_function_code( stack->MinorFunction ) ); switch (stack->MinorFunction) { + case IRP_MN_QUERY_ID: + ret = query_id( device, irp, stack->Parameters.QueryId.IdType ); + break; case IRP_MN_QUERY_CAPABILITIES: { DEVICE_CAPABILITIES *caps = stack->Parameters.DeviceCapabilities.Capabilities; @@ -96,11 +296,21 @@ static NTSTATUS WINAPI pdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) ret = STATUS_SUCCESS; break; case IRP_MN_REMOVE_DEVICE: - IoDeleteDevice( device_obj ); + assert( device->removed ); + winebluetooth_radio_free( device->radio ); + IoDeleteDevice( device->device_obj ); + ret = STATUS_SUCCESS; + break; case IRP_MN_SURPRISE_REMOVAL: - irp->IoStatus.Status = STATUS_SUCCESS; - IoCompleteRequest( irp, IO_NO_INCREMENT ); - return STATUS_SUCCESS; + EnterCriticalSection( &device_list_cs ); + if (!device->removed) + { + device->removed = TRUE; + list_remove( &device->entry ); + } + LeaveCriticalSection( &device_list_cs ); + ret = STATUS_SUCCESS; + break; default: FIXME( "Unhandled minor function %s.\n", debugstr_minor_function_code( stack->MinorFunction ) ); break; @@ -144,7 +354,7 @@ NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) TRACE( "(%p, %s)\n", driver, debugstr_w( path->Buffer ) );
status = winebluetooth_init(); - if (status != STATUS_SUCCESS) + if (status) return status;
driver_obj = driver; diff --git a/dlls/winebth.sys/winebth_priv.h b/dlls/winebth.sys/winebth_priv.h index ca38aea44ac..cbe365b703f 100644 --- a/dlls/winebth.sys/winebth_priv.h +++ b/dlls/winebth.sys/winebth_priv.h @@ -21,6 +21,8 @@ #ifndef __WINE_WINEBTH_WINEBTH_H_ #define __WINE_WINEBTH_WINEBTH_H_
+#include <bthsdpdef.h> +#include <bluetoothapis.h> #include <ddk/wdm.h>
#include <wine/debug.h> @@ -44,6 +46,20 @@ struct string_buffer
#define XX(i) case (i): return #i
+static inline const char *debugstr_BUS_QUERY_ID_TYPE( BUS_QUERY_ID_TYPE type ) +{ + switch (type) + { + XX(BusQueryDeviceID); + XX(BusQueryHardwareIDs); + XX(BusQueryCompatibleIDs); + XX(BusQueryInstanceID); + XX(BusQueryDeviceSerialNumber); + XX(BusQueryContainerID); + default: + return wine_dbg_sprintf( "(unknown %d)", type ); + } +}
static inline const char *debugstr_minor_function_code( UCHAR code ) { @@ -79,6 +95,48 @@ static inline const char *debugstr_minor_function_code( UCHAR code ) } #undef XX
+typedef struct +{ + UINT_PTR handle; +} winebluetooth_radio_t; + +void winebluetooth_radio_free( winebluetooth_radio_t radio ); + +enum winebluetooth_watcher_event_type +{ + BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED, +}; + +struct winebluetooth_watcher_event_radio_added +{ + winebluetooth_radio_t radio; +}; + +union winebluetooth_watcher_event_data +{ + struct winebluetooth_watcher_event_radio_added radio_added; +}; + +struct winebluetooth_watcher_event +{ + enum winebluetooth_watcher_event_type event_type; + union winebluetooth_watcher_event_data event_data; +}; + +enum winebluetooth_event_type +{ + WINEBLUETOOTH_EVENT_WATCHER_EVENT, +}; + +struct winebluetooth_event +{ + enum winebluetooth_event_type status; + union { + struct winebluetooth_watcher_event watcher_event; + } data; +}; + +NTSTATUS winebluetooth_get_event( struct winebluetooth_event *result ); NTSTATUS winebluetooth_init( void ); NTSTATUS winebluetooth_shutdown( void );
From: Vibhav Pant vibhavp@gmail.com
--- dlls/winebth.sys/unixlib.c | 30 +++++++++++++++++ dlls/winebth.sys/unixlib.h | 9 +++++ dlls/winebth.sys/winebluetooth.c | 17 ++++++++++ dlls/winebth.sys/winebth.c | 56 ++++++++++++++++++++++++++++++++ dlls/winebth.sys/winebth_priv.h | 3 ++ 5 files changed, 115 insertions(+)
diff --git a/dlls/winebth.sys/unixlib.c b/dlls/winebth.sys/unixlib.c index 69f3493848f..09641be6396 100644 --- a/dlls/winebth.sys/unixlib.c +++ b/dlls/winebth.sys/unixlib.c @@ -118,6 +118,35 @@ static NTSTATUS bluetooth_shutdown( void *params ) return STATUS_SUCCESS; }
+static NTSTATUS get_unique_name( const struct unix_name *name, char *buf, SIZE_T *buf_size ) +{ + SIZE_T path_len, i; + + path_len = strlen( name->str ); + if (*buf_size <= (path_len * sizeof(char))) + { + *buf_size = (path_len + 1) * sizeof(char); + return STATUS_BUFFER_TOO_SMALL; + } + + for (i = 0; i < path_len; i++) + { + if (name->str[i] == '/') buf[i] = '_'; + else + buf[i] = name->str[i]; + } + buf[path_len] = '\0'; + return STATUS_SUCCESS; +} + +static NTSTATUS bluetooth_adapter_get_unique_name( void *args ) +{ + struct bluetooth_adapter_get_unique_name_params *params = args; + if (!dbus_connection) return STATUS_NOT_SUPPORTED; + + return get_unique_name( params->adapter, params->buf, ¶ms->buf_size ); +} + static NTSTATUS bluetooth_adapter_free( void *args ) { struct bluetooth_adapter_free_params *params = args; @@ -138,6 +167,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = { bluetooth_init, bluetooth_shutdown,
+ bluetooth_adapter_get_unique_name, bluetooth_adapter_free,
bluetooth_get_event, diff --git a/dlls/winebth.sys/unixlib.h b/dlls/winebth.sys/unixlib.h index 46509bfb20a..45a000a2e88 100644 --- a/dlls/winebth.sys/unixlib.h +++ b/dlls/winebth.sys/unixlib.h @@ -48,6 +48,14 @@ struct bluetooth_adapter_free_params unix_name_t adapter; };
+struct bluetooth_adapter_get_unique_name_params +{ + unix_name_t adapter; + + char *buf; + SIZE_T buf_size; +}; + struct bluetooth_get_event_params { struct winebluetooth_event result; @@ -58,6 +66,7 @@ enum bluetoothapis_funcs unix_bluetooth_init, unix_bluetooth_shutdown,
+ unix_bluetooth_adapter_get_unique_name, unix_bluetooth_adapter_free,
unix_bluetooth_get_event, diff --git a/dlls/winebth.sys/winebluetooth.c b/dlls/winebth.sys/winebluetooth.c index c0088577d13..3d5aa77094d 100644 --- a/dlls/winebth.sys/winebluetooth.c +++ b/dlls/winebth.sys/winebluetooth.c @@ -36,6 +36,23 @@
WINE_DEFAULT_DEBUG_CHANNEL( winebth );
+NTSTATUS winebluetooth_radio_get_unique_name( winebluetooth_radio_t radio, char *name, + SIZE_T *size ) +{ + struct bluetooth_adapter_get_unique_name_params params = {0}; + NTSTATUS status; + + TRACE( "(%p, %p, %p)\n", (void *)radio.handle, name, size ); + + params.adapter = radio.handle; + params.buf = name; + params.buf_size = *size; + status = UNIX_BLUETOOTH_CALL( bluetooth_adapter_get_unique_name, ¶ms ); + if (status == STATUS_BUFFER_TOO_SMALL) + *size = params.buf_size; + return status; +} + void winebluetooth_radio_free( winebluetooth_radio_t radio ) { struct bluetooth_adapter_free_params args = { 0 }; diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index 434604b4dad..8de50ed8b45 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -66,6 +66,7 @@ struct bluetooth_radio
DEVICE_OBJECT *device_obj; winebluetooth_radio_t radio; + WCHAR *hw_name; };
void WINAPIV append_id( struct string_buffer *buffer, const WCHAR *format, ... ) @@ -98,7 +99,45 @@ void WINAPIV append_id( struct string_buffer *buffer, const WCHAR *format, ... )
static HANDLE event_loop_thread; +static NTSTATUS radio_get_hw_name_w( winebluetooth_radio_t radio, WCHAR **name ) +{ + char *name_a; + SIZE_T size = sizeof( char ) * 256; + NTSTATUS status; + + name_a = malloc( size ); + if (!name_a) + return STATUS_NO_MEMORY; + + status = winebluetooth_radio_get_unique_name( radio, name_a, &size ); + if (status == STATUS_BUFFER_TOO_SMALL) + { + void *ptr = realloc( name_a, size ); + if (!ptr) + { + free( name_a ); + return STATUS_NO_MEMORY; + } + name_a = ptr; + status = winebluetooth_radio_get_unique_name( radio, name_a, &size ); + } + if (status != STATUS_SUCCESS) + { + free( name_a ); + return status; + }
+ *name = malloc( (mbstowcs( NULL, name_a, 0 ) + 1) * sizeof( WCHAR )); + if (!*name) + { + free( name_a ); + return status; + } + + mbstowcs( *name, name_a, strlen( name_a ) + 1 ); + free( name_a ); + return STATUS_SUCCESS; +} static void add_bluetooth_radio( struct winebluetooth_watcher_event_radio_added event ) { struct bluetooth_radio *device; @@ -106,11 +145,19 @@ static void add_bluetooth_radio( struct winebluetooth_watcher_event_radio_added UNICODE_STRING string; NTSTATUS status; WCHAR name[256]; + WCHAR *hw_name; static unsigned int radio_index;
swprintf( name, ARRAY_SIZE( name ), L"\Device\WINEBTH-RADIO-%d", radio_index++ ); TRACE( "Adding new bluetooth radio %p: %s\n", (void *)event.radio.handle, debugstr_w( name ) );
+ status = radio_get_hw_name_w( event.radio, &hw_name ); + if (status) + { + ERR( "Failed to get hardware name for radio %p, status %#lx\n", (void *)event.radio.handle, status ); + return; + } + RtlInitUnicodeString( &string, name ); status = IoCreateDevice( driver_obj, sizeof( *device ), &string, FILE_DEVICE_BLUETOOTH, 0, FALSE, &device_obj ); @@ -124,6 +171,7 @@ static void add_bluetooth_radio( struct winebluetooth_watcher_event_radio_added device->device_obj = device_obj; device->radio = event.radio; device->removed = FALSE; + device->hw_name = hw_name;
EnterCriticalSection( &device_list_cs ); list_add_tail( &device_list, &device->entry ); @@ -257,6 +305,13 @@ static NTSTATUS query_id(const struct bluetooth_radio *ext, IRP *irp, BUS_QUERY_ TRACE( "(%p, %p, %s)\n", ext, irp, debugstr_BUS_QUERY_ID_TYPE( type ) ); switch (type) { + case BusQueryDeviceID: + append_id( &buf, L"WINEBTH\RADIO" ); + break; + case BusQueryInstanceID: + append_id( &buf, L"%s", ext->hw_name ); + break; + case BusQueryHardwareIDs: case BusQueryCompatibleIDs: append_id( &buf, L"" ); break; @@ -297,6 +352,7 @@ static NTSTATUS WINAPI pdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) break; case IRP_MN_REMOVE_DEVICE: assert( device->removed ); + free( device->hw_name ); winebluetooth_radio_free( device->radio ); IoDeleteDevice( device->device_obj ); ret = STATUS_SUCCESS; diff --git a/dlls/winebth.sys/winebth_priv.h b/dlls/winebth.sys/winebth_priv.h index cbe365b703f..d047dcbc8e8 100644 --- a/dlls/winebth.sys/winebth_priv.h +++ b/dlls/winebth.sys/winebth_priv.h @@ -100,6 +100,9 @@ typedef struct UINT_PTR handle; } winebluetooth_radio_t;
+ +NTSTATUS winebluetooth_radio_get_unique_name( winebluetooth_radio_t radio, char *name, + SIZE_T *size ); void winebluetooth_radio_free( winebluetooth_radio_t radio );
enum winebluetooth_watcher_event_type
From: Vibhav Pant vibhavp@gmail.com
--- dlls/winebth.sys/winebth.c | 20 ++++++++++++++++++++ include/Makefile.in | 1 + include/bthdef.h | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 include/bthdef.h
diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index 8de50ed8b45..ca8b1b4dadb 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -29,6 +29,7 @@ #include <winternl.h> #include <initguid.h> #include <devpkey.h> +#include <bthdef.h> #include <winioctl.h> #include <ddk/wdm.h>
@@ -67,6 +68,8 @@ struct bluetooth_radio DEVICE_OBJECT *device_obj; winebluetooth_radio_t radio; WCHAR *hw_name; + UNICODE_STRING bthport_symlink_name; + UNICODE_STRING bthradio_symlink_name; };
void WINAPIV append_id( struct string_buffer *buffer, const WCHAR *format, ... ) @@ -348,10 +351,27 @@ static NTSTATUS WINAPI pdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) break; } case IRP_MN_START_DEVICE: + if (IoRegisterDeviceInterface( device_obj, &GUID_BTHPORT_DEVICE_INTERFACE, NULL, + &device->bthport_symlink_name ) == STATUS_SUCCESS) + IoSetDeviceInterfaceState( &device->bthport_symlink_name, TRUE ); + + if (IoRegisterDeviceInterface( device_obj, &GUID_BLUETOOTH_RADIO_INTERFACE, NULL, + &device->bthradio_symlink_name ) == STATUS_SUCCESS) + IoSetDeviceInterfaceState( &device->bthradio_symlink_name, TRUE ); ret = STATUS_SUCCESS; break; case IRP_MN_REMOVE_DEVICE: assert( device->removed ); + if (device->bthport_symlink_name.Buffer) + { + IoSetDeviceInterfaceState(&device->bthport_symlink_name, FALSE); + RtlFreeUnicodeString( &device->bthport_symlink_name ); + } + if (device->bthradio_symlink_name.Buffer) + { + IoSetDeviceInterfaceState(&device->bthradio_symlink_name, FALSE); + RtlFreeUnicodeString( &device->bthradio_symlink_name ); + } free( device->hw_name ); winebluetooth_radio_free( device->radio ); IoDeleteDevice( device->device_obj ); diff --git a/include/Makefile.in b/include/Makefile.in index 856a9f79b95..c24ee535211 100644 --- a/include/Makefile.in +++ b/include/Makefile.in @@ -58,6 +58,7 @@ SOURCES = \ bits5_0.idl \ bitsmsg.h \ bluetoothapis.h \ + bthdef.h \ bthsdpdef.h \ cderr.h \ cdosys.idl \ diff --git a/include/bthdef.h b/include/bthdef.h new file mode 100644 index 00000000000..2b81235a776 --- /dev/null +++ b/include/bthdef.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2024 Vibhav Pant + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __BTHDEF_H__ +#define __BTHDEF_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +DEFINE_GUID( GUID_BTHPORT_DEVICE_INTERFACE, 0x850302a, 0xb344, 0x4fda, 0x9b, 0xe9, 0x90, 0x57, 0x6b, + 0x8d, 0x46, 0xf0 ); + +DEFINE_GUID( GUID_BLUETOOTH_RADIO_INTERFACE, 0x92383b0e, 0xf90e, 0x4ac9, 0x8d, 0x44, 0x8c, 0x2d, + 0x0d, 0x0e, 0xbd, 0xa2 ); + +#ifdef __cplusplus +} +#endif + +#endif /* __BTHDEF_H__ */
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=149733
Your paranoid android.
=== debian11b (64 bit WoW report) ===
user32: input.c:4305: Test succeeded inside todo block: button_down_hwnd_todo 1: got MSG_TEST_WIN hwnd 0000000000C600F0, msg WM_LBUTTONDOWN, wparam 0x1, lparam 0x320032 win.c:4073: Test succeeded inside todo block: Expected active window 0000000002D80140, got 0000000002D80140. win.c:4074: Test succeeded inside todo block: Expected focus window 0000000002D80140, got 0000000002D80140.
winmm: mci: Timeout