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.
-- v15: dlls/winebth.sys: Update radio PDO properties on receiving PropertiesChanged for an org.bluez.Adapter1 object. dlls/winebth.sys: Remove the corresponding radio PDO on receiving InterfacesRemoved for a org.bluez.Adapter1 object. dlls/winebth.sys: Create new radio PDOs on receiving InterfacesAdded for objects that implement org.bluez.Adapter1. dlls/winebth.sys: Set radio PDO properties from the device's corresponding org.bluez.Adapter1 object properties. 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
--- configure.ac | 1 + dlls/winebth.sys/Makefile.in | 8 ++ dlls/winebth.sys/winebth.c | 153 ++++++++++++++++++++++++++++++++ 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 + 8 files changed, 280 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/configure.ac b/configure.ac index f5f7c0201f1..a20622606d4 100644 --- a/configure.ac +++ b/configure.ac @@ -3240,6 +3240,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..8ea483039ee --- /dev/null +++ b/dlls/winebth.sys/winebth.c @@ -0,0 +1,153 @@ +/* + * 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: + irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest( irp, IO_NO_INCREMENT ); + return STATUS_SUCCESS; + case IRP_MN_SURPRISE_REMOVAL: + ret = STATUS_SUCCESS; + break; + 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 ) +{ + FIXME( "(%p) stub!\n", 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 456555e28b9..09ad6422af1 100644 --- a/loader/wine.inf.in +++ b/loader/wine.inf.in @@ -2633,6 +2633,7 @@ winebus.inf,"@%12%\winebus.sys,-1" winehid.inf,"@%12%\winehid.sys,-1" wineusb.inf,"@%12%\wineusb.sys,-1" winexinput.inf,"@%12%\winexinput.sys,-1" +winebth.inf,"@%12%\winebth.sys,-1"
[NlsFiles] c_037.nls diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c index 20260f94369..297a1bd6237 100644 --- a/programs/wineboot/wineboot.c +++ b/programs/wineboot/wineboot.c @@ -1546,6 +1546,7 @@ static void install_root_pnp_devices(void) { {"root\wine\winebus", "root\winebus\0", "C:\windows\inf\winebus.inf"}, {"root\wine\wineusb", "root\wineusb\0", "C:\windows\inf\wineusb.inf"}, + {"root\wine\winebth", "root\winebth\0", "C:\windows\inf\winebth.inf"}, }; SP_DEVINFO_DATA device = {sizeof(device)}; unsigned int i;
From: Vibhav Pant vibhavp@gmail.com
--- dlls/winebth.sys/Makefile.in | 7 +- dlls/winebth.sys/dbus.c | 126 +++++++++++++++++++++++++++++++ dlls/winebth.sys/dbus.h | 113 +++++++++++++++++++++++++++ dlls/winebth.sys/unixlib.c | 62 +++++++++++++++ dlls/winebth.sys/unixlib.h | 38 ++++++++++ dlls/winebth.sys/unixlib_priv.h | 40 ++++++++++ dlls/winebth.sys/winebluetooth.c | 51 +++++++++++++ dlls/winebth.sys/winebluetooth.h | 28 +++++++ dlls/winebth.sys/winebth.c | 14 +++- dlls/winebth.sys/winebth_priv.h | 5 ++ 10 files changed, 480 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 create mode 100644 dlls/winebth.sys/winebluetooth.h
diff --git a/dlls/winebth.sys/Makefile.in b/dlls/winebth.sys/Makefile.in index 19f754cb487..5c75de50250 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 = \ winebth.c \ + dbus.c \ + unixlib.c \ + winebluetooth.c \ winebth.rc diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c new file mode 100644 index 00000000000..004d3ac42ad --- /dev/null +++ b/dlls/winebth.sys/dbus.c @@ -0,0 +1,126 @@ +/* + * 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 == NULL) + { + ERR( "Failed to get system dbus connection: %s: %s\n", error.name, error.message ); + p_dbus_error_free( &error ); + return NULL; + } + TRACE_( dbus )( "new dbus connection: %s\n", dbgstr_dbus_connection( connection ) ); + + 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 + +#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..73b3b20aad8 --- /dev/null +++ b/dlls/winebth.sys/unixlib_priv.h @@ -0,0 +1,40 @@ +/* + * 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" + +#ifdef SONAME_LIBDBUS_1 +extern void *bluez_dbus_init( void ); +extern void bluez_dbus_close( void *connection ); +extern void bluez_dbus_free( void *connection ); +#endif /* SONAME_LIBDBUS_1 */ +#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..4fafc68a3d8 --- /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 "winebluetooth.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/winebluetooth.h b/dlls/winebth.sys/winebluetooth.h new file mode 100644 index 00000000000..b22175e22a5 --- /dev/null +++ b/dlls/winebth.sys/winebluetooth.h @@ -0,0 +1,28 @@ +/* + * 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 + * + */ + +#ifndef __WINE_WINEBLUETOOTH_H__ +#define __WINE_WINEBLUETOOTH_H__ + +NTSTATUS winebluetooth_init( void ); +NTSTATUS winebluetooth_shutdown( void ); + +#endif /* __WINE_WINEBLUETOOTH_H__ */ diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index 8ea483039ee..ad5c318b496 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>
@@ -34,6 +36,7 @@ #include <wine/list.h>
#include "winebth_priv.h" +#include "winebluetooth.h"
WINE_DEFAULT_DEBUG_CHANNEL( winebth );
@@ -58,6 +61,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 ); @@ -141,13 +145,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..34e91df5651 100644 --- a/dlls/winebth.sys/winebth_priv.h +++ b/dlls/winebth.sys/winebth_priv.h @@ -32,6 +32,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
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 | 252 +++++++++++++++++++++++++++++++ dlls/winebth.sys/unixlib.c | 93 ++++++++++++ dlls/winebth.sys/unixlib.h | 46 ++++++ dlls/winebth.sys/unixlib_priv.h | 14 ++ dlls/winebth.sys/winebluetooth.c | 44 ++++++ dlls/winebth.sys/winebluetooth.h | 77 ++++++++++ dlls/winebth.sys/winebth.c | 241 +++++++++++++++++++++++++++++ dlls/winebth.sys/winebth_priv.h | 14 ++ 8 files changed, 781 insertions(+)
diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index 004d3ac42ad..53468a53e5e 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 "winebluetooth.h" + #include "unixlib.h" #include "unixlib_priv.h" #include "dbus.h" @@ -51,6 +57,22 @@ WINE_DECLARE_DEBUG_CHANNEL( dbus );
const int bluez_timeout = -1;
+#define DBUS_INTERFACE_OBJECTMANAGER "org.freedesktop.DBus.ObjectManager" + +#define DBUS_INTERFACES_ADDED_SIGNATURE \ + 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 + +#define DBUS_INTERFACES_REMOVED_SIGNATURE \ + DBUS_TYPE_OBJECT_PATH_AS_STRING \ + DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING + #define BLUEZ_DEST "org.bluez" #define BLUEZ_INTERFACE_ADAPTER "org.bluez.Adapter1"
@@ -79,12 +101,77 @@ 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; + /* struct bluez_init_entry */ + struct list initial_device_list; +}; + void *bluez_dbus_init( void ) { DBusError error; @@ -121,6 +208,171 @@ 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 ); + list_init( &watcher_ctx->initial_device_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, + struct list *device_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", 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; + } + init_device->object.radio.radio.handle = radio_name = + unix_name_get_or_create( path ); + if (!radio_name) + { + free( init_device ); + status = STATUS_NO_MEMORY; + goto done; + } + list_add_tail( adapter_list, &init_device->entry ); + TRACE( "Found BlueZ org.bluez.Adapter1 object %s: %p\n", + debugstr_a( radio_name->str ), init_device->object.radio.radio.handle ); + break; + } + } + } + + TRACE( "Initial device list: radios: %d, devices: %d\n", + list_count( adapter_list ), list_count( device_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_loop_result *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_LOOP_STATUS_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, -1 )) + { + 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, + &watcher_ctx->initial_device_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
#endif /* SONAME_LIBDBUS_1 */ diff --git a/dlls/winebth.sys/unixlib.c b/dlls/winebth.sys/unixlib.c index c289a690659..014ac70495a 100644 --- a/dlls/winebth.sys/unixlib.c +++ b/dlls/winebth.sys/unixlib.c @@ -24,17 +24,73 @@
#include <config.h>
+#include <stdlib.h> #include <stdarg.h> +#include <assert.h> +#include <ctype.h> +#include <pthread.h>
#include <ntstatus.h> #define WIN32_NO_STATUS +#include <winternl.h> +#include <winbase.h> +#include <windef.h> #include <wine/debug.h> +#include <wine/list.h> +#include <wine/rbtree.h>
#include "unixlib.h" #include "unixlib_priv.h"
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 NTSTATUS bluetooth_init ( void *params ) @@ -54,9 +110,46 @@ 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_watcher_init( void *args ) +{ + struct bluetooth_watcher_init_params *params = args; + + if (!dbus_connection) return STATUS_NOT_SUPPORTED; + return bluez_watcher_init( dbus_connection, ¶ms->unix_watcher_ctx ); +} + +static NTSTATUS bluetooth_watcher_close( void *args ) +{ + if (!dbus_connection) return STATUS_NOT_SUPPORTED; + return STATUS_SUCCESS; +} + +static NTSTATUS bluetooth_event_loop_once( void *args ) +{ + struct bluetooth_event_loop_once_params *params = args; + + if (!dbus_connection) return STATUS_NOT_SUPPORTED; + memset( ¶ms->result, 0, sizeof( params->result ) ); + return bluez_dbus_loop( dbus_connection, params->unix_watcher_ctx, ¶ms->result ); +} + const unixlib_entry_t __wine_unix_call_funcs[] = { bluetooth_init, bluetooth_shutdown, + + bluetooth_adapter_free, + + bluetooth_watcher_init, + bluetooth_watcher_close, + + bluetooth_event_loop_once, };
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..4ca0c56e906 100644 --- a/dlls/winebth.sys/unixlib.h +++ b/dlls/winebth.sys/unixlib.h @@ -21,15 +21,61 @@ #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 "winebluetooth.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_watcher_init_params +{ + unix_handle_t unix_watcher_ctx; +}; + +struct bluetooth_watcher_close_params +{ + unix_handle_t unix_watcher_ctx; +}; + +struct bluetooth_event_loop_once_params +{ + unix_handle_t unix_watcher_ctx; + + struct winebluetooth_event_loop_result result; +}; + enum bluetoothapis_funcs { unix_bluetooth_init, unix_bluetooth_shutdown,
+ unix_bluetooth_adapter_free, + + unix_bluetooth_watcher_init, + unix_bluetooth_watcher_close, + + unix_bluetooth_event_loop_once, + unix_funcs_count };
diff --git a/dlls/winebth.sys/unixlib_priv.h b/dlls/winebth.sys/unixlib_priv.h index 73b3b20aad8..cd79db3fd25 100644 --- a/dlls/winebth.sys/unixlib_priv.h +++ b/dlls/winebth.sys/unixlib_priv.h @@ -33,8 +33,22 @@ #include "unixlib.h"
#ifdef SONAME_LIBDBUS_1 + +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_loop_result *result ); +extern NTSTATUS bluez_watcher_init( void *connection, void **ctx ); #endif /* SONAME_LIBDBUS_1 */ #endif /* __WINE_WINEBTH_UNIXLIB_PRIV_H */ diff --git a/dlls/winebth.sys/winebluetooth.c b/dlls/winebth.sys/winebluetooth.c index 4fafc68a3d8..d6b36833356 100644 --- a/dlls/winebth.sys/winebluetooth.c +++ b/dlls/winebth.sys/winebluetooth.c @@ -34,6 +34,50 @@ #include "winebluetooth.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_watcher_init( winebluetooth_watcher_t *watcher ) +{ + struct bluetooth_watcher_init_params args = { 0 }; + NTSTATUS status; + + TRACE( "(%p)\n", watcher ); + status = UNIX_BLUETOOTH_CALL( bluetooth_watcher_init, &args ); + watcher->handle = args.unix_watcher_ctx; + return status; +} + +NTSTATUS winebluetooth_watcher_close( winebluetooth_watcher_t watcher ) +{ + struct bluetooth_watcher_close_params args = { .unix_watcher_ctx = watcher.handle }; + + TRACE( "(%p)\n", (void *)watcher.handle ); + return UNIX_BLUETOOTH_CALL( bluetooth_watcher_close, &args ); +} + +NTSTATUS winebluetooth_event_loop_run( winebluetooth_watcher_t watcher, + struct winebluetooth_event_loop_result *result ) +{ + struct bluetooth_event_loop_once_params params = {0}; + NTSTATUS status; + + TRACE( "(%p, %p)\n", (void *)watcher.handle, result ); + + params.unix_watcher_ctx = watcher.handle; + status = UNIX_BLUETOOTH_CALL( bluetooth_event_loop_once, ¶ms ); + *result = params.result; + return status; +} + NTSTATUS winebluetooth_init( void ) { NTSTATUS status; diff --git a/dlls/winebth.sys/winebluetooth.h b/dlls/winebth.sys/winebluetooth.h index b22175e22a5..2a978e64a00 100644 --- a/dlls/winebth.sys/winebluetooth.h +++ b/dlls/winebth.sys/winebluetooth.h @@ -22,6 +22,83 @@ #ifndef __WINE_WINEBLUETOOTH_H__ #define __WINE_WINEBLUETOOTH_H__
+#include <bthsdpdef.h> +#include <bluetoothapis.h> + +typedef struct +{ +#ifdef WINE_UNIX_LIB + void *handle; +#else + UINT_PTR handle; +#endif +} winebluetooth_radio_t; + +union winebluetooth_property +{ + BOOL boolean; + ULONG ulong; + BLUETOOTH_ADDRESS address; + WCHAR name[BLUETOOTH_MAX_NAME_SIZE]; +}; + +struct winebluetooth_bluetooth_version +{ + UCHAR major; + UCHAR minor; +}; + +void winebluetooth_radio_free( winebluetooth_radio_t radio ); + +typedef struct +{ +#ifdef WINE_UNIX_LIB + void *handle; +#else + UINT_PTR handle; +#endif +} winebluetooth_watcher_t; + +enum winebluetooth_watcher_event_type +{ + BLUETOOTH_WATCHER_EVENT_TYPE_NONE, + 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; +}; + +NTSTATUS winebluetooth_watcher_init( winebluetooth_watcher_t *watcher ); +NTSTATUS winebluetooth_watcher_close( winebluetooth_watcher_t watcher ); + +enum winebluetooth_event_loop_status +{ + WINEBLUETOOTH_EVENT_LOOP_STATUS_WATCHER_EVENT, +}; + +struct winebluetooth_event_loop_result +{ + enum winebluetooth_event_loop_status status; + union { + struct winebluetooth_watcher_event watcher_event; + } data; +}; + +NTSTATUS winebluetooth_event_loop_run( winebluetooth_watcher_t watcher, + struct winebluetooth_event_loop_result *result ); NTSTATUS winebluetooth_init( void ); NTSTATUS winebluetooth_shutdown( void );
diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index ad5c318b496..ab62d97ce17 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -44,6 +44,150 @@ 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 ); + +static winebluetooth_watcher_t watcher; +struct bluetooth_radio +{ + struct list entry; + BOOL removed; + + DEVICE_OBJECT *device_obj; + winebluetooth_radio_t radio; + LIST_ENTRY irp_list; +}; + +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 != STATUS_SUCCESS) + { + 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; + InitializeListHead(&device->irp_list); + + EnterCriticalSection( &device_list_cs ); + list_add_tail( &device_list, &device->entry ); + LeaveCriticalSection( &device_list_cs ); + + IoInvalidateDeviceRelations( bus_pdo, BusRelations ); +} + +static void remove_pending_irps(struct bluetooth_radio *device) +{ + LIST_ENTRY *entry; + IRP *irp; + + while ((entry = RemoveHeadList( &device->irp_list )) != &device->irp_list) + { + irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); + irp->IoStatus.Status = STATUS_DELETE_PENDING; + irp->IoStatus.Information = 0; + IoCompleteRequest(irp, IO_NO_INCREMENT); + } +} + +static DWORD CALLBACK bluetooth_event_loop_thread_proc( void *arg ) +{ + NTSTATUS status; + while (TRUE) + { + struct winebluetooth_event_loop_result result = {0}; + + status = winebluetooth_event_loop_run( watcher, &result ); + if (status != STATUS_PENDING) break; + + switch (result.status) + { + case WINEBLUETOOTH_EVENT_LOOP_STATUS_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" ); + winebluetooth_watcher_close( watcher ); + return 0; +} + static NTSTATUS WINAPI fdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) { IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp ); @@ -52,7 +196,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: @@ -60,8 +241,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 ); @@ -76,14 +269,43 @@ 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 BusQueryInstanceID: + append_id(&buf, L"%p", ext->radio); + break; + case BusQueryCompatibleIDs: + append_id( &buf, L"WINEBTH\WINE_COMP_BTH" ); + 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; @@ -97,10 +319,22 @@ static NTSTATUS WINAPI pdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) ret = STATUS_SUCCESS; break; case IRP_MN_REMOVE_DEVICE: + assert( device->removed ); + remove_pending_irps( device ); + winebluetooth_radio_free( device->radio ); + IoDeleteDevice( device->device_obj ); irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest( irp, IO_NO_INCREMENT ); return STATUS_SUCCESS; case IRP_MN_SURPRISE_REMOVAL: + remove_pending_irps( device ); + EnterCriticalSection( &device_list_cs ); + if (!device->removed) + { + device->removed = TRUE; + list_remove( &device->entry ); + } + LeaveCriticalSection( &device_list_cs ); ret = STATUS_SUCCESS; break; default: @@ -152,6 +386,13 @@ NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) if (status != STATUS_SUCCESS) return status;
+ status = winebluetooth_watcher_init( &watcher ); + if (status != STATUS_SUCCESS) + { + ERR( "Failed to initialize bluetooth watcher: %#lx\n", status ); + return status; + } + driver_obj = driver;
driver->DriverExtension->AddDevice = driver_add_device; diff --git a/dlls/winebth.sys/winebth_priv.h b/dlls/winebth.sys/winebth_priv.h index 34e91df5651..e73586fead3 100644 --- a/dlls/winebth.sys/winebth_priv.h +++ b/dlls/winebth.sys/winebth_priv.h @@ -40,6 +40,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 ) {
From: Vibhav Pant vibhavp@gmail.com
--- dlls/winebth.sys/unixlib.c | 30 ++++++++++++++++++ dlls/winebth.sys/unixlib.h | 9 ++++++ dlls/winebth.sys/winebluetooth.c | 19 +++++++++++ dlls/winebth.sys/winebluetooth.h | 2 ++ dlls/winebth.sys/winebth.c | 54 ++++++++++++++++++++++++++++++++ 5 files changed, 114 insertions(+)
diff --git a/dlls/winebth.sys/unixlib.c b/dlls/winebth.sys/unixlib.c index 014ac70495a..17fedf8137a 100644 --- a/dlls/winebth.sys/unixlib.c +++ b/dlls/winebth.sys/unixlib.c @@ -110,6 +110,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] = toupper( 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; @@ -144,6 +173,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = { bluetooth_init, bluetooth_shutdown,
+ bluetooth_adapter_get_unique_name, bluetooth_adapter_free,
bluetooth_watcher_init, diff --git a/dlls/winebth.sys/unixlib.h b/dlls/winebth.sys/unixlib.h index 4ca0c56e906..1f22abcdb6a 100644 --- a/dlls/winebth.sys/unixlib.h +++ b/dlls/winebth.sys/unixlib.h @@ -47,6 +47,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_watcher_init_params { unix_handle_t unix_watcher_ctx; @@ -69,6 +77,7 @@ enum bluetoothapis_funcs unix_bluetooth_init, unix_bluetooth_shutdown,
+ unix_bluetooth_adapter_get_unique_name, unix_bluetooth_adapter_free,
unix_bluetooth_watcher_init, diff --git a/dlls/winebth.sys/winebluetooth.c b/dlls/winebth.sys/winebluetooth.c index d6b36833356..cbce950c39f 100644 --- a/dlls/winebth.sys/winebluetooth.c +++ b/dlls/winebth.sys/winebluetooth.c @@ -36,6 +36,25 @@
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/winebluetooth.h b/dlls/winebth.sys/winebluetooth.h index 2a978e64a00..4b870910d6c 100644 --- a/dlls/winebth.sys/winebluetooth.h +++ b/dlls/winebth.sys/winebluetooth.h @@ -48,6 +48,8 @@ struct winebluetooth_bluetooth_version UCHAR minor; };
+NTSTATUS winebluetooth_radio_get_unique_name( winebluetooth_radio_t radio, char *name, + SIZE_T *size ); void winebluetooth_radio_free( winebluetooth_radio_t radio );
typedef struct diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index ab62d97ce17..4d3527a2311 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -68,6 +68,7 @@ struct bluetooth_radio
DEVICE_OBJECT *device_obj; winebluetooth_radio_t radio; + WCHAR *hw_name; LIST_ENTRY irp_list; };
@@ -101,7 +102,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 == NULL) + 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 == NULL) + { + 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 == NULL) + { + 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; @@ -109,11 +148,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 != STATUS_SUCCESS) + { + 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 ); @@ -127,6 +174,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; InitializeListHead(&device->irp_list);
EnterCriticalSection( &device_list_cs ); @@ -276,9 +324,14 @@ 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\%s", ext->hw_name ); + break; case BusQueryInstanceID: append_id(&buf, L"%p", ext->radio); break; + case BusQueryHardwareIDs: + append_id( &buf, L"WINEBTH\%s", ext->hw_name ); case BusQueryCompatibleIDs: append_id( &buf, L"WINEBTH\WINE_COMP_BTH" ); append_id( &buf, L"" ); @@ -321,6 +374,7 @@ static NTSTATUS WINAPI pdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) case IRP_MN_REMOVE_DEVICE: assert( device->removed ); remove_pending_irps( device ); + free( device->hw_name ); winebluetooth_radio_free( device->radio ); IoDeleteDevice( device->device_obj ); irp->IoStatus.Status = STATUS_SUCCESS;
From: Vibhav Pant vibhavp@gmail.com
--- dlls/winebth.sys/winebth.c | 21 +++++++++++++++++++++ include/Makefile.in | 2 ++ include/bthdef.h | 33 +++++++++++++++++++++++++++++++++ include/ddk/bthguid.h | 30 ++++++++++++++++++++++++++++++ 4 files changed, 86 insertions(+) create mode 100644 include/bthdef.h create mode 100644 include/ddk/bthguid.h
diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index 4d3527a2311..dbb9a765aef 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -29,8 +29,10 @@ #include <winternl.h> #include <initguid.h> #include <devpkey.h> +#include <bthdef.h> #include <winioctl.h> #include <ddk/wdm.h> +#include <ddk/bthguid.h>
#include <wine/debug.h> #include <wine/list.h> @@ -69,6 +71,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; LIST_ENTRY irp_list; };
@@ -369,11 +373,28 @@ 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 ); remove_pending_irps( device ); + 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 50673cb761e..e351d3e5a4b 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 \ @@ -192,6 +193,7 @@ SOURCES = \ dde.h \ dde.rh \ ddeml.h \ + ddk/bthguid.h \ ddk/compstui.h \ ddk/csq.h \ ddk/d3dkmthk.h \ diff --git a/include/bthdef.h b/include/bthdef.h new file mode 100644 index 00000000000..9b65cc8d48f --- /dev/null +++ b/include/bthdef.h @@ -0,0 +1,33 @@ +/* + * 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 ); + +#ifdef __cplusplus +} +#endif + +#endif /* __BTHDEF_H__ */ diff --git a/include/ddk/bthguid.h b/include/ddk/bthguid.h new file mode 100644 index 00000000000..f565fc177d5 --- /dev/null +++ b/include/ddk/bthguid.h @@ -0,0 +1,30 @@ +/* + * GUID definitions used by winebth.sys + * + * 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 __DDK_BTHGUID_H__ +#define __DDK_BTHGUID_H__ + +/* Not sure how this is any different from GUID_BTHPORT_DEVICE_INTERFACE, but it's used as the + * enumeration ID by windows.devices.bluetooth (see dlls/windows.devices.bluetooth/tests/bluetooth.c). */ +DEFINE_GUID( GUID_BLUETOOTH_RADIO_INTERFACE, 0x92383b0e, 0xf90e, 0x4ac9, 0x8d, 0x44, 0x8c, 0x2d, + 0x0d, 0x0e, 0xbd, 0xa2 ); + +#endif
From: Vibhav Pant vibhavp@gmail.com
--- dlls/winebth.sys/dbus.c | 91 ++++++++++++++++++++++++++++++++ dlls/winebth.sys/winebluetooth.h | 34 ++++++++++++ dlls/winebth.sys/winebth.c | 27 ++++++++++ include/ddk/bthguid.h | 25 +++++++++ 4 files changed, 177 insertions(+)
diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index 53468a53e5e..2a395d13e5e 100644 --- a/dlls/winebth.sys/dbus.c +++ b/dlls/winebth.sys/dbus.c @@ -162,6 +162,89 @@ static NTSTATUS bluez_get_objects_async( DBusConnection *connection, DBusPending DBUS_DICT_ENTRY_END_CHAR_AS_STRING \ DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+ +static void parse_mac_address( const char *addr_str, BYTE dest[6] ) +{ + int addr[6], i; + + sscanf( addr_str, "%x:%x:%x:%x:%x:%x", &addr[0], &addr[1], &addr[2], &addr[3], &addr[4], + &addr[5] ); + for (i = 0 ; i < 6; i++) + dest[i] = addr[i]; +} + +static void bluez_radio_prop_from_dict_entry( const char *prop_name, DBusMessageIter *variant, + struct winebluetooth_radio_properties *props, + 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, wanted_props_mask ); + + if (wanted_props_mask & WINEBLUETOOTH_RADIO_PROPERTY_ADDRESS && + !strcmp( prop_name, "Address" ) && + p_dbus_message_iter_get_arg_type( variant ) == DBUS_TYPE_STRING) + { + const char *addr_str; + p_dbus_message_iter_get_basic( variant, &addr_str ); + parse_mac_address( addr_str, props->address.rgBytes ); + *props_mask |= WINEBLUETOOTH_RADIO_PROPERTY_ADDRESS; + } + else if (wanted_props_mask & WINEBLUETOOTH_RADIO_PROPERTY_CLASS && + !strcmp( prop_name, "Class" ) && + p_dbus_message_iter_get_arg_type( variant ) == DBUS_TYPE_UINT32) + { + dbus_uint32_t class; + p_dbus_message_iter_get_basic( variant, &class ); + props->class = class; + *props_mask |= WINEBLUETOOTH_RADIO_PROPERTY_CLASS; + } + else if (wanted_props_mask & WINEBLUETOOTH_RADIO_PROPERTY_MANUFACTURER && + !strcmp( prop_name, "Manufacturer" ) && + p_dbus_message_iter_get_arg_type( variant ) == DBUS_TYPE_UINT16) + { + dbus_uint16_t manufacturer; + p_dbus_message_iter_get_basic( variant, &manufacturer ); + props->manufacturer = manufacturer; + *props_mask |= WINEBLUETOOTH_RADIO_PROPERTY_MANUFACTURER; + } + else if (wanted_props_mask & WINEBLUETOOTH_RADIO_PROPERTY_CONNECTABLE && + !strcmp( prop_name, "Connectable" ) && + p_dbus_message_iter_get_arg_type( variant ) == DBUS_TYPE_BOOLEAN) + { + dbus_bool_t connectable; + p_dbus_message_iter_get_basic( variant, &connectable ); + props->connectable = connectable != 0; + *props_mask |= WINEBLUETOOTH_RADIO_PROPERTY_CONNECTABLE; + } + else if (wanted_props_mask & WINEBLUETOOTH_RADIO_PROPERTY_DISCOVERABLE && + !strcmp( prop_name, "Discoverable" ) && + p_dbus_message_iter_get_arg_type( variant ) == DBUS_TYPE_BOOLEAN) + { + dbus_bool_t discoverable; + p_dbus_message_iter_get_basic( variant, &discoverable ); + props->discoverable = discoverable != 0; + *props_mask |= WINEBLUETOOTH_RADIO_PROPERTY_DISCOVERABLE; + } + else if (wanted_props_mask & WINEBLUETOOTH_RADIO_PROPERTY_DISCOVERING && + !strcmp( prop_name, "Discovering") && + p_dbus_message_iter_get_arg_type( variant ) == DBUS_TYPE_BOOLEAN) + { + dbus_bool_t discovering; + p_dbus_message_iter_get_basic( variant, &discovering ); + props->discovering = discovering != 0; + *props_mask |= WINEBLUETOOTH_RADIO_PROPERTY_DISCOVERING; + } + else if (wanted_props_mask & WINEBLUETOOTH_RADIO_PROPERTY_PAIRABLE && + !strcmp( prop_name, "Pairable" ) && + p_dbus_message_iter_get_arg_type( variant ) == DBUS_TYPE_BOOLEAN) + { + dbus_bool_t pairable; + p_dbus_message_iter_get_basic( variant, &pairable ); + props->pairable = pairable != 0; + *props_mask |= WINEBLUETOOTH_RADIO_PROPERTY_PAIRABLE; + } +} + struct bluez_watcher_ctx { void *init_device_list_call; @@ -271,6 +354,8 @@ static NTSTATUS bluez_build_initial_device_lists( DBusMessage *reply, struct lis { if (!strcmp( iface, BLUEZ_INTERFACE_ADAPTER )) { + const char *prop_name; + DBusMessageIter variant; struct bluez_init_entry *init_device = calloc( 1, sizeof( *init_device ) ); struct unix_name *radio_name;
@@ -287,6 +372,12 @@ static NTSTATUS bluez_build_initial_device_lists( DBusMessage *reply, struct lis status = STATUS_NO_MEMORY; goto done; } + while ((prop_name = bluez_next_dict_entry( &prop_iter, &variant ))) + { + bluez_radio_prop_from_dict_entry( + prop_name, &variant, &init_device->object.radio.props, + &init_device->object.radio.props_mask, WINEBLUETOOTH_RADIO_ALL_PROPERTIES ); + } list_add_tail( adapter_list, &init_device->entry ); TRACE( "Found BlueZ org.bluez.Adapter1 object %s: %p\n", debugstr_a( radio_name->str ), init_device->object.radio.radio.handle ); diff --git a/dlls/winebth.sys/winebluetooth.h b/dlls/winebth.sys/winebluetooth.h index 4b870910d6c..21293e8ba4b 100644 --- a/dlls/winebth.sys/winebluetooth.h +++ b/dlls/winebth.sys/winebluetooth.h @@ -34,6 +34,25 @@ typedef struct #endif } winebluetooth_radio_t;
+typedef UINT16 winebluetooth_radio_props_mask_t; + +#define WINEBLUETOOTH_RADIO_PROPERTY_NAME (1) +#define WINEBLUETOOTH_RADIO_PROPERTY_ADDRESS (1 << 2) +#define WINEBLUETOOTH_RADIO_PROPERTY_DISCOVERABLE (1 << 3) +#define WINEBLUETOOTH_RADIO_PROPERTY_CONNECTABLE (1 << 4) +#define WINEBLUETOOTH_RADIO_PROPERTY_CLASS (1 << 5) +#define WINEBLUETOOTH_RADIO_PROPERTY_MANUFACTURER (1 << 6) +#define WINEBLUETOOTH_RADIO_PROPERTY_VERSION (1 << 7) +#define WINEBLUETOOTH_RADIO_PROPERTY_DISCOVERING (1 << 8) +#define WINEBLUETOOTH_RADIO_PROPERTY_PAIRABLE (1 << 9) + +#define WINEBLUETOOTH_RADIO_ALL_PROPERTIES \ + (WINEBLUETOOTH_RADIO_PROPERTY_NAME | WINEBLUETOOTH_RADIO_PROPERTY_ADDRESS | \ + WINEBLUETOOTH_RADIO_PROPERTY_DISCOVERABLE | WINEBLUETOOTH_RADIO_PROPERTY_CONNECTABLE | \ + WINEBLUETOOTH_RADIO_PROPERTY_CLASS | WINEBLUETOOTH_RADIO_PROPERTY_MANUFACTURER | \ + WINEBLUETOOTH_RADIO_PROPERTY_VERSION | WINEBLUETOOTH_RADIO_PROPERTY_DISCOVERING | \ + WINEBLUETOOTH_RADIO_PROPERTY_PAIRABLE) + union winebluetooth_property { BOOL boolean; @@ -48,6 +67,19 @@ struct winebluetooth_bluetooth_version UCHAR minor; };
+struct winebluetooth_radio_properties +{ + BOOL discoverable; + BOOL connectable; + BOOL discovering; + BOOL pairable; + BLUETOOTH_ADDRESS address; + CHAR name[BLUETOOTH_MAX_NAME_SIZE]; + ULONG class; + USHORT manufacturer; + struct winebluetooth_bluetooth_version version; +}; + NTSTATUS winebluetooth_radio_get_unique_name( winebluetooth_radio_t radio, char *name, SIZE_T *size ); void winebluetooth_radio_free( winebluetooth_radio_t radio ); @@ -69,6 +101,8 @@ enum winebluetooth_watcher_event_type
struct winebluetooth_watcher_event_radio_added { + winebluetooth_radio_props_mask_t props_mask; + struct winebluetooth_radio_properties props; winebluetooth_radio_t radio; };
diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index dbb9a765aef..f0745b7a866 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -69,6 +69,8 @@ struct bluetooth_radio BOOL removed;
DEVICE_OBJECT *device_obj; + winebluetooth_radio_props_mask_t props_mask; + struct winebluetooth_radio_properties props; winebluetooth_radio_t radio; WCHAR *hw_name; UNICODE_STRING bthport_symlink_name; @@ -179,6 +181,8 @@ static void add_bluetooth_radio( struct winebluetooth_watcher_event_radio_added device->radio = event.radio; device->removed = FALSE; device->hw_name = hw_name; + device->props = event.props; + device->props_mask = event.props_mask; InitializeListHead(&device->irp_list);
EnterCriticalSection( &device_list_cs ); @@ -373,6 +377,29 @@ static NTSTATUS WINAPI pdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) break; } case IRP_MN_START_DEVICE: + if (device->props_mask & WINEBLUETOOTH_RADIO_PROPERTY_ADDRESS) + { + union + { + UINT64 uint; + BYTE addr[8]; + } radio_addr; + + memset( &radio_addr, 0, sizeof( radio_addr ) ); + memcpy( &radio_addr.addr[2], device->props.address.rgBytes, + sizeof( device->props.address.rgBytes ) ); + IoSetDevicePropertyData( device_obj, &DEVPKEY_BluetoothRadio_Address, + LOCALE_NEUTRAL, 0, DEVPROP_TYPE_UINT64, + sizeof( radio_addr ), &radio_addr ); + } + if (device->props_mask & WINEBLUETOOTH_RADIO_PROPERTY_MANUFACTURER) + { + UINT16 manufacturer = device->props.manufacturer; + IoSetDevicePropertyData( device_obj, &DEVPKEY_BluetoothRadio_Manufacturer, + LOCALE_NEUTRAL, 0, DEVPROP_TYPE_UINT16, + sizeof( manufacturer ), &manufacturer ); + } + if (IoRegisterDeviceInterface( device_obj, &GUID_BTHPORT_DEVICE_INTERFACE, NULL, &device->bthport_symlink_name ) == STATUS_SUCCESS) IoSetDeviceInterfaceState( &device->bthport_symlink_name, TRUE ); diff --git a/include/ddk/bthguid.h b/include/ddk/bthguid.h index f565fc177d5..ce0f4adfad5 100644 --- a/include/ddk/bthguid.h +++ b/include/ddk/bthguid.h @@ -27,4 +27,29 @@ DEFINE_GUID( GUID_BLUETOOTH_RADIO_INTERFACE, 0x92383b0e, 0xf90e, 0x4ac9, 0x8d, 0x44, 0x8c, 0x2d, 0x0d, 0x0e, 0xbd, 0xa2 );
+#define DEFINE_BTH_RADIO_DEVPROPKEY( d, i ) \ + DEFINE_DEVPROPKEY( DEVPKEY_BluetoothRadio_##d, 0xa92f26ca, 0xeda7, 0x4b1d, 0x9d, 0xb2, 0x27, \ + 0xb6, 0x8a, 0xa5, 0xa2, 0xeb, ( i ) ) + +DEFINE_BTH_RADIO_DEVPROPKEY( Address, 1 ); /* DEVPROP_TYPE_UINT64 */ +DEFINE_BTH_RADIO_DEVPROPKEY( Manufacturer, 2 ); /* DEVPROP_TYPE_UINT16 */ +DEFINE_BTH_RADIO_DEVPROPKEY( LMPSupportedFeatures, 3 ); /* DEVPROP_TYPE_UINT64 */ +DEFINE_BTH_RADIO_DEVPROPKEY( LMPVersion, 4 ); /* DEVPROP_TYPE_BYTE */ +DEFINE_BTH_RADIO_DEVPROPKEY( HCIVendorFeatures, 8 ); /* DEVPROP_TYPE_UINT64 */ +DEFINE_BTH_RADIO_DEVPROPKEY( MaximumAdvertisementDataLength, 17 ); /* DEVPROP_TYPE_UINT16 */ +DEFINE_BTH_RADIO_DEVPROPKEY( LELocalSupportedFeatures, 22 ); /* DEVPROP_TYPE_UINT64 */ + + +#ifdef WINE_BTH_EXTENSIONS +#define DEFINE_WINEBTH_RADIO_DEVPROPKEY( d, i ) \ + DEFINE_DEVPROPKEY( DEVPKEY_WineBluetooth_Radio_##d, 0x9ccd2c1a, 0x4b06, 0x4ecf, 0xb1, 0xd5, \ + 0x35, 0x62, 0xd6, 0xb0, 0xda, 0xac, (i) ) + +/* Authentication I/O capability as currently advertised by the radio. DEVPROP_TYPE_BYTE */ +DEFINE_WINEBTH_RADIO_DEVPROPKEY( Capability, 1 ); + +#undef DEFINE_BTH_RADIO_DEVPROPKEY +#endif /* WINE_BTH_EXTENSIONS */ + + #endif
From: Vibhav Pant vibhavp@gmail.com
The unix bluetooth watcher uses a message filter on the DBus connection to listen for "InterfacesAdded" signals on DBus. If such a signal is received for an object, and the list of interfaces includes org.bluez.Adapter1, queue a BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED event. --- dlls/winebth.sys/dbus.c | 228 ++++++++++++++++++++++++++++++++ dlls/winebth.sys/unixlib.c | 3 + dlls/winebth.sys/unixlib_priv.h | 1 + 3 files changed, 232 insertions(+)
diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index 2a395d13e5e..5b708caa427 100644 --- a/dlls/winebth.sys/dbus.c +++ b/dlls/winebth.sys/dbus.c @@ -58,6 +58,7 @@ WINE_DECLARE_DEBUG_CHANNEL( dbus ); const int bluez_timeout = -1;
#define DBUS_INTERFACE_OBJECTMANAGER "org.freedesktop.DBus.ObjectManager" +#define DBUS_OBJECTMANAGER_SIGNAL_INTERFACESADDED "InterfacesAdded"
#define DBUS_INTERFACES_ADDED_SIGNATURE \ DBUS_TYPE_OBJECT_PATH_AS_STRING \ @@ -101,6 +102,31 @@ failed: return FALSE; }
+static NTSTATUS bluez_dbus_error_to_ntstatus( const DBusError *error ) +{ + +#define DBUS_ERROR_CASE(n, s) if(p_dbus_error_has_name( error, (n)) ) return (s) + + DBUS_ERROR_CASE( "org.bluez.Error.Failed", STATUS_INTERNAL_ERROR); + DBUS_ERROR_CASE( "org.bluez.Error.NotReady", STATUS_DEVICE_NOT_READY ); + DBUS_ERROR_CASE( "org.bluez.Error.NotAuthorized", STATUS_ACCESS_DENIED ); + DBUS_ERROR_CASE( "org.bluez.Error.InvalidArguments", STATUS_INVALID_PARAMETER ); + DBUS_ERROR_CASE( "org.bluez.Error.AlreadyExists", STATUS_NO_MORE_ENTRIES ); + DBUS_ERROR_CASE( "org.bluez.Error.AuthenticationCanceled", STATUS_CANCELLED ); + DBUS_ERROR_CASE( "org.bluez.Error.AuthenticationFailed", STATUS_INTERNAL_ERROR ); + DBUS_ERROR_CASE( "org.bluez.Error.AuthenticationRejected", STATUS_INTERNAL_ERROR ); + DBUS_ERROR_CASE( "org.bluez.Error.AuthenticationTimeout", STATUS_TIMEOUT ); + DBUS_ERROR_CASE( "org.bluez.Error.ConnectionAttemptFailed", STATUS_DEVICE_NOT_CONNECTED); + DBUS_ERROR_CASE( "org.bluez.Error.NotConnected", STATUS_DEVICE_NOT_CONNECTED ); + DBUS_ERROR_CASE( "org.bluez.Error.InProgress", STATUS_OPERATION_IN_PROGRESS ); + DBUS_ERROR_CASE( DBUS_ERROR_UNKNOWN_OBJECT, STATUS_INVALID_PARAMETER ); + DBUS_ERROR_CASE( DBUS_ERROR_NO_MEMORY, STATUS_NO_MEMORY ); + DBUS_ERROR_CASE( DBUS_ERROR_NOT_SUPPORTED, STATUS_NOT_SUPPORTED ); + DBUS_ERROR_CASE( DBUS_ERROR_ACCESS_DENIED, STATUS_ACCESS_DENIED ); + return STATUS_INTERNAL_ERROR; +#undef DBUS_ERROR_CASE +} + static const char *bluez_next_dict_entry( DBusMessageIter *iter, DBusMessageIter *variant ) { DBusMessageIter sub; @@ -117,6 +143,39 @@ static const char *bluez_next_dict_entry( DBusMessageIter *iter, DBusMessageIter return name; }
+static const char *dbgstr_dbus_message( DBusMessage *message ) +{ + const char *interface; + const char *member; + const char *path; + const char *sender; + const char *signature; + int type; + + interface = p_dbus_message_get_interface( message ); + member = p_dbus_message_get_member( message ); + path = p_dbus_message_get_path( message ); + sender = p_dbus_message_get_sender( message ); + type = p_dbus_message_get_type( message ); + signature = p_dbus_message_get_signature( message ); + + switch (type) + { + case DBUS_MESSAGE_TYPE_METHOD_CALL: + return wine_dbg_sprintf( + "{method_call sender=%s interface=%s member=%s path=%s signature='%s'}", + debugstr_a( sender ), debugstr_a( interface ), debugstr_a( member ), debugstr_a( path ), + debugstr_a( signature ) ); + case DBUS_MESSAGE_TYPE_SIGNAL: + return wine_dbg_sprintf( "{signal sender=%s interface=%s member=%s path=%s signature='%s'}", + debugstr_a( sender ), debugstr_a( interface ), + debugstr_a( member ), debugstr_a( path ), + debugstr_a( signature ) ); + default: + return wine_dbg_sprintf( "%p", message ); + } +} + static inline const char *dbgstr_dbus_connection( DBusConnection *connection ) { return wine_dbg_sprintf( "{%p connected=%d}", connection, @@ -253,6 +312,9 @@ struct bluez_watcher_ctx struct list initial_radio_list; /* struct bluez_init_entry */ struct list initial_device_list; + + /* struct bluez_watcher_event */ + struct list event_list; };
void *bluez_dbus_init( void ) @@ -299,12 +361,119 @@ struct bluez_watcher_event union winebluetooth_watcher_event_data event; };
+static BOOL bluez_event_list_queue_new_event( struct list *event_list, + enum winebluetooth_watcher_event_type event_type, + union winebluetooth_watcher_event_data event ) +{ + struct bluez_watcher_event *event_entry; + + event_entry = calloc(1, sizeof( *event_entry ) ); + if (!event_entry) + { + ERR( "Could not allocate memory for DBus event.\n" ); + return FALSE; + } + + event_entry->event_type = event_type; + event_entry->event = event; + list_add_tail( event_list, &event_entry->entry ); + + return TRUE; +} + +static DBusHandlerResult bluez_filter( DBusConnection *conn, DBusMessage *msg, void *user_data ) +{ + struct list *event_list; + SIZE_T init_count, final_count; + + if (TRACE_ON( dbus )) + { + TRACE_( dbus ) + ( "(%s, %s, %p)\n", dbgstr_dbus_connection( conn ), dbgstr_dbus_message( msg ), user_data ); + } + + event_list = &((struct bluez_watcher_ctx *)user_data)->event_list; + init_count = list_count( event_list ); + + if (p_dbus_message_is_signal( msg, DBUS_INTERFACE_OBJECTMANAGER, DBUS_OBJECTMANAGER_SIGNAL_INTERFACESADDED ) + && p_dbus_message_has_signature( msg, DBUS_INTERFACES_ADDED_SIGNATURE )) + { + DBusMessageIter iter, ifaces_iter; + const char *object_path; + + p_dbus_message_iter_init (msg, &iter); + p_dbus_message_iter_get_basic( &iter, &object_path ); + p_dbus_message_iter_next( &iter ); + p_dbus_message_iter_recurse(&iter, &ifaces_iter); + while (p_dbus_message_iter_has_next( &ifaces_iter )) + { + DBusMessageIter iface_entry; + const char *iface_name; + + p_dbus_message_iter_recurse( &ifaces_iter, &iface_entry ); + p_dbus_message_iter_get_basic( &iface_entry, &iface_name ); + if (strcmp( iface_name, BLUEZ_INTERFACE_ADAPTER ) == 0) + { + struct winebluetooth_watcher_event_radio_added radio_added = {0}; + struct unix_name *radio; + DBusMessageIter props_iter, variant; + const char *prop_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_radio_prop_from_dict_entry( prop_name, &variant, &radio_added.props, + &radio_added.props_mask, + WINEBLUETOOTH_RADIO_ALL_PROPERTIES ); + } + + radio_added.radio.handle = radio = unix_name_get_or_create( object_path ); + if (radio_added.radio.handle == NULL) + { + ERR("failed to allocate memory for adapter path %s\n", object_path); + break; + } + else + { + union winebluetooth_watcher_event_data event = { .radio_added = radio_added }; + TRACE( "New BlueZ %s object added at %s: %p\n", BLUEZ_INTERFACE_ADAPTER, + object_path, radio_added.radio.handle ); + if (!bluez_event_list_queue_new_event( + event_list, BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED, event )) + { + unix_name_free( radio ); + } + } + } + p_dbus_message_iter_next( &ifaces_iter ); + } + } + + final_count = list_count( event_list ); + assert( final_count >= init_count ); + if (final_count > init_count) + TRACE( "Received %ld new watcher-related event(s) from BlueZ\n", (final_count - init_count) ); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static const char BLUEZ_MATCH_OBJECTMANAGER[] = "type='signal'," + "interface='org.freedesktop.DBus.ObjectManager'," + "sender='"BLUEZ_DEST"'," + "path='/'"; + +static const char *BLUEZ_MATCH_RULES[] = { BLUEZ_MATCH_OBJECTMANAGER }; + NTSTATUS bluez_watcher_init( void *connection, void **ctx ) { + DBusError err; NTSTATUS status; DBusPendingCall *call; struct bluez_watcher_ctx *watcher_ctx = calloc( 1, sizeof( struct bluez_watcher_ctx ) ); + SIZE_T i;
if (watcher_ctx == NULL) return STATUS_NO_MEMORY; status = bluez_get_objects_async( connection, &call ); @@ -318,11 +487,46 @@ NTSTATUS bluez_watcher_init( void *connection, void **ctx ) list_init( &watcher_ctx->initial_radio_list ); list_init( &watcher_ctx->initial_device_list );
+ list_init( &watcher_ctx->event_list ); + + if (!p_dbus_connection_add_filter( connection, bluez_filter, watcher_ctx, free )) + { + p_dbus_pending_call_cancel( call ); + p_dbus_pending_call_unref( call ); + free( watcher_ctx ); + ERR( "could not add DBus filter\n" ); + return STATUS_NO_MEMORY; + } + p_dbus_error_init( &err ); + for (i = 0; i < ARRAY_SIZE( BLUEZ_MATCH_RULES ); i++) + { + TRACE( "Adding DBus match rule "%s"\n", BLUEZ_MATCH_RULES[i] ); + + p_dbus_bus_add_match( connection, BLUEZ_MATCH_RULES[i], &err ); + if (p_dbus_error_is_set( &err )) + { + NTSTATUS status = bluez_dbus_error_to_ntstatus( &err ); + ERR( "could not add DBus match "%s": '%s': '%s'\n", BLUEZ_MATCH_RULES[i], err.name, + err.message ); + p_dbus_pending_call_cancel( call ); + p_dbus_pending_call_unref( call ); + p_dbus_error_free( &err ); + free( watcher_ctx ); + return status; + } + } + p_dbus_error_free( &err ); *ctx = watcher_ctx; TRACE( "ctx=%p\n", ctx ); return STATUS_SUCCESS; }
+void bluez_watcher_close( void *connection, void *ctx ) +{ + p_dbus_bus_remove_match( connection, BLUEZ_MATCH_OBJECTMANAGER, NULL ); + p_dbus_connection_remove_filter( connection, bluez_filter, ctx ); +} + struct bluez_init_entry { union { @@ -401,6 +605,17 @@ static struct bluez_init_entry *bluez_init_entries_list_pop( struct list *list ) return device; }
+static struct bluez_watcher_event *bluez_watcher_event_queue_ready( struct bluez_watcher_ctx *ctx ) +{ + struct list *head = list_head( &ctx->event_list ); + struct bluez_watcher_event *event = LIST_ENTRY(head, struct bluez_watcher_event, entry); + TRACE("(%p)\n", ctx); + + list_remove( &event->entry ); + return event; + return NULL; +} + NTSTATUS bluez_dbus_loop( void *c, void *watcher, struct winebluetooth_event_loop_result *result ) { @@ -412,6 +627,8 @@ NTSTATUS bluez_dbus_loop( void *c, void *watcher,
while (TRUE) { + struct bluez_watcher_event *bluez_event; + if (!list_empty( &watcher_ctx->initial_radio_list )) { struct bluez_init_entry *radio = @@ -425,6 +642,17 @@ NTSTATUS bluez_dbus_loop( void *c, void *watcher, p_dbus_connection_unref( connection ); return STATUS_PENDING; } + else if ((bluez_event = bluez_watcher_event_queue_ready( watcher_ctx ))) + { + struct winebluetooth_watcher_event *watcher_event = &result->data.watcher_event; + + result->status = WINEBLUETOOTH_EVENT_LOOP_STATUS_WATCHER_EVENT; + watcher_event->event_type = bluez_event->event_type; + watcher_event->event_data = bluez_event->event; + free( bluez_event ); + p_dbus_connection_unref( connection ); + return STATUS_PENDING; + } else if (!p_dbus_connection_read_write_dispatch( connection, -1 )) { p_dbus_connection_unref( connection ); diff --git a/dlls/winebth.sys/unixlib.c b/dlls/winebth.sys/unixlib.c index 17fedf8137a..d6ae2c37851 100644 --- a/dlls/winebth.sys/unixlib.c +++ b/dlls/winebth.sys/unixlib.c @@ -156,7 +156,10 @@ static NTSTATUS bluetooth_watcher_init( void *args )
static NTSTATUS bluetooth_watcher_close( void *args ) { + struct bluetooth_watcher_close_params *params = args; + if (!dbus_connection) return STATUS_NOT_SUPPORTED; + bluez_watcher_close(dbus_connection, params->unix_watcher_ctx); return STATUS_SUCCESS; }
diff --git a/dlls/winebth.sys/unixlib_priv.h b/dlls/winebth.sys/unixlib_priv.h index cd79db3fd25..9131bd31e34 100644 --- a/dlls/winebth.sys/unixlib_priv.h +++ b/dlls/winebth.sys/unixlib_priv.h @@ -50,5 +50,6 @@ 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_loop_result *result ); extern NTSTATUS bluez_watcher_init( void *connection, void **ctx ); +extern void bluez_watcher_close( void *connection, void *ctx ); #endif /* SONAME_LIBDBUS_1 */ #endif /* __WINE_WINEBTH_UNIXLIB_PRIV_H */
From: Vibhav Pant vibhavp@gmail.com
--- dlls/winebth.sys/dbus.c | 46 ++++++++++++++++++++++++++++++++ dlls/winebth.sys/winebluetooth.h | 6 +++++ dlls/winebth.sys/winebth.c | 25 +++++++++++++++++ 3 files changed, 77 insertions(+)
diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index 5b708caa427..890d8d94603 100644 --- a/dlls/winebth.sys/dbus.c +++ b/dlls/winebth.sys/dbus.c @@ -59,6 +59,7 @@ const int bluez_timeout = -1;
#define DBUS_INTERFACE_OBJECTMANAGER "org.freedesktop.DBus.ObjectManager" #define DBUS_OBJECTMANAGER_SIGNAL_INTERFACESADDED "InterfacesAdded" +#define DBUS_OBJECTMANAGER_SIGNAL_INTERFACESREMOVED "InterfacesRemoved"
#define DBUS_INTERFACES_ADDED_SIGNATURE \ DBUS_TYPE_OBJECT_PATH_AS_STRING \ @@ -450,6 +451,51 @@ static DBusHandlerResult bluez_filter( DBusConnection *conn, DBusMessage *msg, v p_dbus_message_iter_next( &ifaces_iter ); } } + else if (p_dbus_message_is_signal( msg, DBUS_INTERFACE_OBJECTMANAGER, DBUS_OBJECTMANAGER_SIGNAL_INTERFACESREMOVED ) + && p_dbus_message_has_signature( msg, DBUS_INTERFACES_REMOVED_SIGNATURE )) + { + const char *object_path; + char **interfaces; + int n_interfaces, i; + DBusError error; + dbus_bool_t success; + + p_dbus_error_init( &error ); + success = p_dbus_message_get_args( msg, &error, DBUS_TYPE_OBJECT_PATH, &object_path, + DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &interfaces, + &n_interfaces, DBUS_TYPE_INVALID ); + if (!success) + { + ERR_( dbus ) + ( "error getting arguments from message: '%s: '%s'\n", error.name, error.message ); + p_dbus_error_free( &error ); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + p_dbus_error_free( &error ); + for (i = 0; i < n_interfaces; i++) + { + if (strcmp( interfaces[i], BLUEZ_INTERFACE_ADAPTER ) == 0) + { + winebluetooth_radio_t radio; + union winebluetooth_watcher_event_data event; + radio.handle = unix_name_get_or_create( object_path ); + + if (!radio.handle) + { + ERR( "failed to allocate memory for adapter path %s\n", object_path ); + continue; + } + event.radio_removed = radio; + if (!bluez_event_list_queue_new_event( + event_list, BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_REMOVED, event )) + { + unix_name_free(radio.handle); + } + } + } + p_dbus_free_string_array( interfaces ); + }
final_count = list_count( event_list ); assert( final_count >= init_count ); diff --git a/dlls/winebth.sys/winebluetooth.h b/dlls/winebth.sys/winebluetooth.h index 21293e8ba4b..f386d6bf69b 100644 --- a/dlls/winebth.sys/winebluetooth.h +++ b/dlls/winebth.sys/winebluetooth.h @@ -83,6 +83,10 @@ struct winebluetooth_radio_properties NTSTATUS winebluetooth_radio_get_unique_name( winebluetooth_radio_t radio, char *name, SIZE_T *size ); void winebluetooth_radio_free( winebluetooth_radio_t radio ); +static inline BOOL winebluetooth_radio_equal( winebluetooth_radio_t r1, winebluetooth_radio_t r2 ) +{ + return r1.handle == r2.handle; +}
typedef struct { @@ -97,6 +101,7 @@ enum winebluetooth_watcher_event_type { BLUETOOTH_WATCHER_EVENT_TYPE_NONE, BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED, + BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_REMOVED, };
struct winebluetooth_watcher_event_radio_added @@ -109,6 +114,7 @@ struct winebluetooth_watcher_event_radio_added union winebluetooth_watcher_event_data { struct winebluetooth_watcher_event_radio_added radio_added; + winebluetooth_radio_t radio_removed; };
struct winebluetooth_watcher_event diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index f0745b7a866..f556fadcc2b 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -192,6 +192,28 @@ static void add_bluetooth_radio( struct winebluetooth_watcher_event_radio_added IoInvalidateDeviceRelations( bus_pdo, BusRelations ); }
+static void remove_bluetooth_radio( winebluetooth_radio_t radio ) +{ + struct bluetooth_radio *device; + + EnterCriticalSection(&device_list_cs); + LIST_FOR_EACH_ENTRY(device, &device_list, struct bluetooth_radio, entry) + { + if (winebluetooth_radio_equal(radio, device->radio) && !device->removed) + { + TRACE( "Removing bluetooth radio %p\n", (void *)radio.handle ); + device->removed = TRUE; + list_remove( &device->entry ); + IoInvalidateDeviceRelations( device->device_obj, BusRelations ); + break; + } + } + LeaveCriticalSection( &device_list_cs ); + + IoInvalidateDeviceRelations( bus_pdo, BusRelations ); + winebluetooth_radio_free( radio ); +} + static void remove_pending_irps(struct bluetooth_radio *device) { LIST_ENTRY *entry; @@ -226,6 +248,9 @@ static DWORD CALLBACK bluetooth_event_loop_thread_proc( void *arg ) case BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED: add_bluetooth_radio( event->event_data.radio_added ); break; + case BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_REMOVED: + remove_bluetooth_radio( event->event_data.radio_removed ); + break; default: FIXME( "Unknown bluetooth watcher event code: %#x\n", event->event_type ); }
From: Vibhav Pant vibhavp@gmail.com
--- dlls/winebth.sys/dbus.c | 228 ++++++++++++++++++++++++++++++- dlls/winebth.sys/winebluetooth.h | 11 ++ dlls/winebth.sys/winebth.c | 125 ++++++++++++++--- include/ddk/bthguid.h | 11 +- 4 files changed, 345 insertions(+), 30 deletions(-)
diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index 890d8d94603..5cfd1068a48 100644 --- a/dlls/winebth.sys/dbus.c +++ b/dlls/winebth.sys/dbus.c @@ -75,6 +75,16 @@ const int bluez_timeout = -1; DBUS_TYPE_OBJECT_PATH_AS_STRING \ DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING
+#define DBUS_PROPERTIES_SIGNAL_PROPERTIESCHANGED "PropertiesChanged" + +#define DBUS_PROPERTIES_CHANGED_SIGNATURE \ + 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_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING + #define BLUEZ_DEST "org.bluez" #define BLUEZ_INTERFACE_ADAPTER "org.bluez.Adapter1"
@@ -305,6 +315,29 @@ static void bluez_radio_prop_from_dict_entry( const char *prop_name, DBusMessage } }
+static NTSTATUS bluez_adapter_get_props_async( void *connection, const char *radio_object_path, + DBusPendingCall **call ) +{ + DBusMessage *request; + static const char *adapter_iface = BLUEZ_INTERFACE_ADAPTER; + dbus_bool_t success; + + request = p_dbus_message_new_method_call( BLUEZ_DEST, radio_object_path, + DBUS_INTERFACE_PROPERTIES, "GetAll" ); + if (!request) return STATUS_NO_MEMORY; + + p_dbus_message_append_args( request, DBUS_TYPE_STRING, &adapter_iface, DBUS_TYPE_INVALID ); + + success = p_dbus_connection_send_with_reply( connection, request, call, bluez_timeout ); + p_dbus_message_unref( request ); + if (!success) + return STATUS_NO_MEMORY; + if (!*call) + return STATUS_INTERNAL_ERROR; + + return STATUS_SUCCESS; +} + struct bluez_watcher_ctx { void *init_device_list_call; @@ -360,14 +393,24 @@ struct bluez_watcher_event struct list entry; enum winebluetooth_watcher_event_type event_type; union winebluetooth_watcher_event_data event; + + /* Some DBus signals, like PropertiesChanged in org.freedesktop.DBus.Properties, require us to + * perform an additional call to get the complete state of the object (in this instance, call + * Get/GetAll to get the values of invalidated properties). The event is queued out only once + * this call completes. */ + DBusPendingCall *pending_call; };
-static BOOL bluez_event_list_queue_new_event( struct list *event_list, - enum winebluetooth_watcher_event_type event_type, - union winebluetooth_watcher_event_data event ) +static BOOL bluez_event_list_queue_new_event_with_call( + struct list *event_list, enum winebluetooth_watcher_event_type event_type, + union winebluetooth_watcher_event_data event, DBusPendingCall *call, + DBusPendingCallNotifyFunction callback ) { struct bluez_watcher_event *event_entry;
+ if (call != NULL) assert( callback != NULL ); + else assert( callback == NULL ); + event_entry = calloc(1, sizeof( *event_entry ) ); if (!event_entry) { @@ -377,11 +420,86 @@ static BOOL bluez_event_list_queue_new_event( struct list *event_list,
event_entry->event_type = event_type; event_entry->event = event; + event_entry->pending_call = call; + if (call != NULL) + p_dbus_pending_call_set_notify(call, callback, &event_entry->event, NULL); list_add_tail( event_list, &event_entry->entry );
return TRUE; }
+static BOOL bluez_event_list_queue_new_event( struct list *event_list, + enum winebluetooth_watcher_event_type event_type, + union winebluetooth_watcher_event_data event ) +{ + return bluez_event_list_queue_new_event_with_call( event_list, event_type, event, NULL, NULL ); +} + +static void bluez_filter_radio_props_changed_callback( DBusPendingCall *call, void *user_data ) +{ + union winebluetooth_watcher_event_data *event = user_data; + struct winebluetooth_watcher_event_radio_props_changed *changed = &event->radio_props_changed; + const struct unix_name *radio = event->radio_props_changed.radio.handle; + DBusMessage *reply = p_dbus_pending_call_steal_reply( call ); + DBusMessageIter dict, prop_iter, variant; + const char *prop_name; + DBusError error; + + TRACE( "call %p, radio %s\n", call, debugstr_a( changed->radio.handle ) ); + + p_dbus_error_init( &error ); + if (p_dbus_set_error_from_message( &error, reply )) + { + ERR( "Failed to get adapter properties for %s: %s: %s\n", debugstr_a( radio->str ), + debugstr_a( error.name ), debugstr_a( error.message ) ); + p_dbus_error_free( &error ); + p_dbus_message_unref( reply ); + return; + } + p_dbus_error_free( &error ); + + p_dbus_message_iter_init( reply, &dict ); + p_dbus_message_iter_recurse( &dict, &prop_iter ); + while((prop_name = bluez_next_dict_entry( &prop_iter, &variant ))) + { + bluez_radio_prop_from_dict_entry( prop_name, &variant, &changed->props, + &changed->changed_props_mask, changed->invalid_props_mask ); + } + changed->invalid_props_mask &= ~changed->changed_props_mask; + p_dbus_message_unref( reply ); +} + +struct bluez_object_property_masks +{ + const char *prop_name; + UINT16 mask; +}; + +static UINT16 bluez_dbus_get_invalidated_properties_from_iter( + DBusMessageIter *invalid_prop_iter, const struct bluez_object_property_masks *prop_masks, + SIZE_T len ) +{ + UINT16 mask = 0; + + while (p_dbus_message_iter_has_next( invalid_prop_iter )) + { + const char *prop_name; + SIZE_T i; + + assert( p_dbus_message_iter_get_arg_type( invalid_prop_iter ) == DBUS_TYPE_STRING ); + p_dbus_message_iter_get_basic( invalid_prop_iter, &prop_name ); + for (i = 0; i < len; i++) + { + if (strcmp( prop_masks[i].prop_name, prop_name ) == 0) + mask |= prop_masks[i].mask; + } + + p_dbus_message_iter_next( invalid_prop_iter ); + } + + return mask; +} + static DBusHandlerResult bluez_filter( DBusConnection *conn, DBusMessage *msg, void *user_data ) { struct list *event_list; @@ -496,6 +614,98 @@ static DBusHandlerResult bluez_filter( DBusConnection *conn, DBusMessage *msg, v } p_dbus_free_string_array( interfaces ); } + else if (p_dbus_message_is_signal( msg, DBUS_INTERFACE_PROPERTIES, DBUS_PROPERTIES_SIGNAL_PROPERTIESCHANGED ) && + p_dbus_message_has_signature( msg, DBUS_PROPERTIES_CHANGED_SIGNATURE )) + { + DBusMessageIter iter; + const char *iface; + + p_dbus_message_iter_init( msg, &iter ); + p_dbus_message_iter_get_basic( &iter, &iface ); + assert( iface != NULL ); + + if (strcmp( iface, BLUEZ_INTERFACE_ADAPTER ) == 0) + { + struct winebluetooth_watcher_event_radio_props_changed props_changed = {0}; + struct unix_name *radio; + DBusMessageIter changed_props_iter, invalid_props_iter, variant; + const char *prop_name; + const static struct bluez_object_property_masks radio_prop_masks[] = { + { "Name", WINEBLUETOOTH_RADIO_PROPERTY_NAME }, + { "Address", WINEBLUETOOTH_RADIO_PROPERTY_ADDRESS }, + { "Discoverable", WINEBLUETOOTH_RADIO_PROPERTY_DISCOVERABLE }, + { "Connectable", WINEBLUETOOTH_RADIO_PROPERTY_CONNECTABLE }, + { "Class", WINEBLUETOOTH_RADIO_PROPERTY_CLASS }, + { "Manufacturer", WINEBLUETOOTH_RADIO_PROPERTY_MANUFACTURER }, + { "Version", WINEBLUETOOTH_RADIO_PROPERTY_VERSION }, + { "Discovering", WINEBLUETOOTH_RADIO_PROPERTY_DISCOVERING }, + { "Pairable", WINEBLUETOOTH_RADIO_PROPERTY_PAIRABLE }, + }; + const char *object_path = p_dbus_message_get_path( msg ); + + radio = unix_name_get_or_create( object_path ); + if (!radio) + { + ERR( "failed to allocate memory for adapter path %s\n", debugstr_a( object_path ) ); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + p_dbus_message_iter_next( &iter ); + p_dbus_message_iter_recurse( &iter, &changed_props_iter ); + while ((prop_name = bluez_next_dict_entry( &changed_props_iter, &variant ))) + { + bluez_radio_prop_from_dict_entry( prop_name, &variant, &props_changed.props, + &props_changed.changed_props_mask, + WINEBLUETOOTH_RADIO_ALL_PROPERTIES ); + } + + p_dbus_message_iter_next( &iter ); + p_dbus_message_iter_recurse( &iter, &invalid_props_iter ); + props_changed.invalid_props_mask = bluez_dbus_get_invalidated_properties_from_iter( + &invalid_props_iter, radio_prop_masks, ARRAY_SIZE( radio_prop_masks ) ); + + TRACE( "Properties changed for radio %s, changed %#x, invalid %#x\n", + debugstr_a( radio->str ), props_changed.changed_props_mask, + props_changed.invalid_props_mask ); + if (props_changed.invalid_props_mask != 0) + { + DBusPendingCall *pending_call = NULL; + union winebluetooth_watcher_event_data event = { .radio_props_changed = props_changed }; + NTSTATUS status = bluez_adapter_get_props_async( conn, radio->str, &pending_call ); + + if (status != STATUS_SUCCESS) + { + ERR( "Failed to create async call to get adapter properties: %#x\n", + (int)status ); + unix_name_free( radio ); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + if (!bluez_event_list_queue_new_event_with_call( event_list, + BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_PROPERTIES_CHANGED, + event, pending_call, + bluez_filter_radio_props_changed_callback )) + { + unix_name_free( radio ); + p_dbus_pending_call_cancel( pending_call ); + p_dbus_pending_call_unref( pending_call ); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + } + else + { + union winebluetooth_watcher_event_data event = { .radio_props_changed = + props_changed }; + if (!bluez_event_list_queue_new_event( event_list, + BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_PROPERTIES_CHANGED, + event )) + { + unix_name_free( radio ); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + } + } + }
final_count = list_count( event_list ); assert( final_count >= init_count ); @@ -654,11 +864,15 @@ static struct bluez_init_entry *bluez_init_entries_list_pop( struct list *list ) static struct bluez_watcher_event *bluez_watcher_event_queue_ready( struct bluez_watcher_ctx *ctx ) { struct list *head = list_head( &ctx->event_list ); - struct bluez_watcher_event *event = LIST_ENTRY(head, struct bluez_watcher_event, entry); - TRACE("(%p)\n", ctx); + struct bluez_watcher_event *event = LIST_ENTRY( head, struct bluez_watcher_event, entry );
- list_remove( &event->entry ); - return event; + if (head == NULL) return NULL; + if (event->pending_call == NULL || p_dbus_pending_call_get_completed( event->pending_call )) + { + list_remove( &event->entry ); + if (event->pending_call) p_dbus_pending_call_unref( event->pending_call ); + return event; + } return NULL; }
diff --git a/dlls/winebth.sys/winebluetooth.h b/dlls/winebth.sys/winebluetooth.h index f386d6bf69b..606104042ef 100644 --- a/dlls/winebth.sys/winebluetooth.h +++ b/dlls/winebth.sys/winebluetooth.h @@ -102,6 +102,7 @@ enum winebluetooth_watcher_event_type BLUETOOTH_WATCHER_EVENT_TYPE_NONE, BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED, BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_REMOVED, + BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_PROPERTIES_CHANGED, };
struct winebluetooth_watcher_event_radio_added @@ -111,10 +112,20 @@ struct winebluetooth_watcher_event_radio_added winebluetooth_radio_t radio; };
+struct winebluetooth_watcher_event_radio_props_changed +{ + winebluetooth_radio_props_mask_t changed_props_mask; + struct winebluetooth_radio_properties props; + + winebluetooth_radio_props_mask_t invalid_props_mask; + winebluetooth_radio_t radio; +}; + union winebluetooth_watcher_event_data { struct winebluetooth_watcher_event_radio_added radio_added; winebluetooth_radio_t radio_removed; + struct winebluetooth_watcher_event_radio_props_changed radio_props_changed; };
struct winebluetooth_watcher_event diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index f556fadcc2b..cf0a1fec7dd 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -22,6 +22,7 @@ #include <stdlib.h> #include <assert.h>
+#define WINE_BTH_EXTENSIONS #include <ntstatus.h> #define WIN32_NO_STATUS #include <windef.h> @@ -214,6 +215,32 @@ static void remove_bluetooth_radio( winebluetooth_radio_t radio ) winebluetooth_radio_free( radio ); }
+static void bluetooth_radio_set_properties( DEVICE_OBJECT *obj, + winebluetooth_radio_props_mask_t mask, + struct winebluetooth_radio_properties *props ); + +static void update_bluetooth_radio_properties( struct winebluetooth_watcher_event_radio_props_changed event ) +{ + struct bluetooth_radio *device; + winebluetooth_radio_t radio = event.radio; + winebluetooth_radio_props_mask_t mask = event.changed_props_mask; + struct winebluetooth_radio_properties props = event.props; + + EnterCriticalSection( &device_list_cs ); + LIST_FOR_EACH_ENTRY( device, &device_list, struct bluetooth_radio, entry ) + { + if (winebluetooth_radio_equal( radio, device->radio ) && !device->removed) + { + device->props_mask = mask; + device->props = props; + bluetooth_radio_set_properties( device->device_obj, device->props_mask, + &device->props ); + break; + } + } + LeaveCriticalSection( &device_list_cs ); +} + static void remove_pending_irps(struct bluetooth_radio *device) { LIST_ENTRY *entry; @@ -251,6 +278,9 @@ static DWORD CALLBACK bluetooth_event_loop_thread_proc( void *arg ) case BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_REMOVED: remove_bluetooth_radio( event->event_data.radio_removed ); break; + case BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_PROPERTIES_CHANGED: + update_bluetooth_radio_properties( event->event_data.radio_props_changed ); + break; default: FIXME( "Unknown bluetooth watcher event code: %#x\n", event->event_type ); } @@ -380,6 +410,78 @@ static NTSTATUS query_id(const struct bluetooth_radio *ext, IRP *irp, BUS_QUERY_ return STATUS_SUCCESS; }
+static void bluetooth_radio_set_properties( DEVICE_OBJECT *obj, + winebluetooth_radio_props_mask_t mask, + struct winebluetooth_radio_properties *props ) +{ + if (mask & WINEBLUETOOTH_RADIO_PROPERTY_ADDRESS) + { + union + { + UINT64 uint; + BYTE addr[8]; + } radio_addr = {0}; + memcpy(&radio_addr.addr[2], props->address.rgBytes, sizeof( props->address.rgBytes )); + IoSetDevicePropertyData( obj, &DEVPKEY_BluetoothRadio_Address, LOCALE_NEUTRAL, 0, + DEVPROP_TYPE_UINT64, sizeof( radio_addr ), &radio_addr ); + } + if (mask & WINEBLUETOOTH_RADIO_PROPERTY_MANUFACTURER) + { + UINT16 manufacturer = props->manufacturer; + IoSetDevicePropertyData( obj, &DEVPKEY_BluetoothRadio_Manufacturer, LOCALE_NEUTRAL, + 0, DEVPROP_TYPE_UINT16, sizeof( manufacturer ), &manufacturer ); + } + if (mask & WINEBLUETOOTH_RADIO_PROPERTY_DISCOVERABLE) + { + BOOL discoverable = props->discoverable; + IoSetDevicePropertyData( obj, &DEVPKEY_WineBluetooth_Radio_Discoverable, LOCALE_NEUTRAL, 0, + DEVPROP_TYPE_BOOLEAN, sizeof( BOOL ), &discoverable ); + } + if (mask & WINEBLUETOOTH_RADIO_PROPERTY_PAIRABLE) + { + BOOL discoverable = props->pairable; + IoSetDevicePropertyData( obj, &DEVPKEY_WineBluetooth_Radio_Pairable, LOCALE_NEUTRAL, 0, + DEVPROP_TYPE_BOOLEAN, sizeof( BOOL ), &discoverable ); + } + if (mask & WINEBLUETOOTH_RADIO_PROPERTY_CONNECTABLE) + { + BOOL connectable = props->connectable; + IoSetDevicePropertyData( obj, &DEVPKEY_WineBluetooth_Radio_Connectable, LOCALE_NEUTRAL, 0, + DEVPROP_TYPE_BOOLEAN, sizeof( BOOL ), &connectable ); + } + if (mask & WINEBLUETOOTH_RADIO_PROPERTY_DISCOVERING) + { + BOOL discovering = props->discovering; + IoSetDevicePropertyData( obj, &DEVPKEY_WineBluetooth_Radio_Discovering, LOCALE_NEUTRAL, 0, + DEVPROP_TYPE_BOOLEAN, sizeof( BOOL ), &discovering ); + } + if (mask & WINEBLUETOOTH_RADIO_PROPERTY_CLASS) + { + UINT32 class = props->class; + IoSetDevicePropertyData( obj, &DEVPKEY_WineBluetooth_Radio_Class, LOCALE_NEUTRAL, 0, + DEVPROP_TYPE_UINT32, sizeof( UINT32 ), &class ); + } + if (mask & WINEBLUETOOTH_RADIO_PROPERTY_NAME) + { + WCHAR *name; + size_t count = mbstowcs( NULL, props->name, 0 ); + size_t size = min( count + 1, BLUETOOTH_MAX_NAME_SIZE * sizeof( WCHAR ) ); + if (count == -1) + { + ERR( "error converting Bluetooth radio name to wide string: %s\n", + debugstr_a( props->name ) ); + return; + } + + name = malloc( size ); + mbstowcs( name, props->name, size ); + name[size - 1] = '\0'; + IoSetDevicePropertyData( obj, &DEVPKEY_Device_FriendlyName, LOCALE_NEUTRAL, 0, + DEVPROP_TYPE_STRING, size, &name ); + free( name ); + } +} + static NTSTATUS WINAPI pdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) { IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp); @@ -402,28 +504,7 @@ static NTSTATUS WINAPI pdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) break; } case IRP_MN_START_DEVICE: - if (device->props_mask & WINEBLUETOOTH_RADIO_PROPERTY_ADDRESS) - { - union - { - UINT64 uint; - BYTE addr[8]; - } radio_addr; - - memset( &radio_addr, 0, sizeof( radio_addr ) ); - memcpy( &radio_addr.addr[2], device->props.address.rgBytes, - sizeof( device->props.address.rgBytes ) ); - IoSetDevicePropertyData( device_obj, &DEVPKEY_BluetoothRadio_Address, - LOCALE_NEUTRAL, 0, DEVPROP_TYPE_UINT64, - sizeof( radio_addr ), &radio_addr ); - } - if (device->props_mask & WINEBLUETOOTH_RADIO_PROPERTY_MANUFACTURER) - { - UINT16 manufacturer = device->props.manufacturer; - IoSetDevicePropertyData( device_obj, &DEVPKEY_BluetoothRadio_Manufacturer, - LOCALE_NEUTRAL, 0, DEVPROP_TYPE_UINT16, - sizeof( manufacturer ), &manufacturer ); - } + bluetooth_radio_set_properties( device_obj, device->props_mask, &device->props );
if (IoRegisterDeviceInterface( device_obj, &GUID_BTHPORT_DEVICE_INTERFACE, NULL, &device->bthport_symlink_name ) == STATUS_SUCCESS) diff --git a/include/ddk/bthguid.h b/include/ddk/bthguid.h index ce0f4adfad5..0dd86f7fc12 100644 --- a/include/ddk/bthguid.h +++ b/include/ddk/bthguid.h @@ -47,7 +47,16 @@ DEFINE_BTH_RADIO_DEVPROPKEY( LELocalSupportedFeatures, 22 ); /* DEVPROP_TY
/* Authentication I/O capability as currently advertised by the radio. DEVPROP_TYPE_BYTE */ DEFINE_WINEBTH_RADIO_DEVPROPKEY( Capability, 1 ); - +/* If device discovery is active for this radio. DEVPROP_TYPE_BOOLEAN */ +DEFINE_WINEBTH_RADIO_DEVPROPKEY( Discovering, 2 ); +/* If the radio is pariable. DEVPROP_TYPE_BOOLEAN */ +DEFINE_WINEBTH_RADIO_DEVPROPKEY( Pairable, 3 ); +/* If the radio is discoverable/visible. DEVPROP_TYPE_BOOLEAN */ +DEFINE_WINEBTH_RADIO_DEVPROPKEY( Discoverable, 4 ); +/* If the radio is connectable. DEVPROP_TYPE_BOOLEAN */ +DEFINE_WINEBTH_RADIO_DEVPROPKEY( Connectable, 5 ); +/* The class of device for this radio. DEVPROP_TYPE_UINT32 */ +DEFINE_WINEBTH_RADIO_DEVPROPKEY( Class, 6 ); #undef DEFINE_BTH_RADIO_DEVPROPKEY #endif /* WINE_BTH_EXTENSIONS */
On Thu Oct 17 03:05:17 2024 +0000, Elizabeth Figura wrote:
What's this type for?
Yeah, it's no longer used. Removed.
On Thu Oct 17 03:05:18 2024 +0000, Elizabeth Figura wrote:
This doesn't seem to be used anywhere?
Fixed, it's meant to be used to verify the GetManagedObjects reply.