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.
-- v19: 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.
From: Vibhav Pant vibhavp@gmail.com
--- configure.ac | 1 + dlls/winebth.sys/Makefile.in | 8 ++ dlls/winebth.sys/winebth.c | 149 ++++++++++++++++++++++++++++++++ dlls/winebth.sys/winebth.inf | 22 +++++ dlls/winebth.sys/winebth.rc | 20 +++++ dlls/winebth.sys/winebth_priv.h | 74 ++++++++++++++++ loader/wine.inf.in | 1 + programs/wineboot/wineboot.c | 1 + 8 files changed, 276 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..27808483e02 --- /dev/null +++ b/dlls/winebth.sys/winebth.c @@ -0,0 +1,149 @@ +/* + * Bluetooth bus driver + * + * Copyright 2024 Vibhav Pant + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <stdarg.h> +#include <stdlib.h> +#include <assert.h> + +#include <ntstatus.h> +#define WIN32_NO_STATUS +#include <windef.h> +#include <winbase.h> +#include <winternl.h> +#include <winioctl.h> +#include <ddk/wdm.h> + +#include <wine/debug.h> +#include <wine/list.h> + +#include "winebth_priv.h" + +WINE_DEFAULT_DEBUG_CHANNEL( winebth ); + +static DRIVER_OBJECT *driver_obj; + +static DEVICE_OBJECT *bus_fdo, *bus_pdo; + +static NTSTATUS WINAPI fdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) +{ + IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp ); + + TRACE( "irp %p, minor function %s.\n", irp, debugstr_minor_function_code( stack->MinorFunction ) ); + + switch (stack->MinorFunction) + { + case IRP_MN_START_DEVICE: + irp->IoStatus.Status = STATUS_SUCCESS; + break; + case IRP_MN_SURPRISE_REMOVAL: + irp->IoStatus.Status = STATUS_SUCCESS; + break; + case IRP_MN_REMOVE_DEVICE: + { + NTSTATUS ret; + IoSkipCurrentIrpStackLocation( irp ); + ret = IoCallDriver( bus_pdo, irp ); + IoDetachDevice( bus_pdo ); + IoDeleteDevice( bus_fdo ); + return ret; + } + default: + FIXME( "Unhandled minor function %s.\n", debugstr_minor_function_code( stack->MinorFunction ) ); + } + + IoSkipCurrentIrpStackLocation( irp ); + return IoCallDriver( bus_pdo, irp ); +} + +static NTSTATUS WINAPI pdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) +{ + IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp); + NTSTATUS ret = irp->IoStatus.Status; + + TRACE( "device_obj %p, irp %p, minor function %s\n", device_obj, irp, debugstr_minor_function_code( stack->MinorFunction ) ); + switch (stack->MinorFunction) + { + case IRP_MN_QUERY_CAPABILITIES: + { + DEVICE_CAPABILITIES *caps = stack->Parameters.DeviceCapabilities.Capabilities; + caps->Removable = TRUE; + caps->SurpriseRemovalOK = TRUE; + caps->RawDeviceOK = TRUE; + ret = STATUS_SUCCESS; + break; + } + case IRP_MN_START_DEVICE: + ret = STATUS_SUCCESS; + break; + case IRP_MN_REMOVE_DEVICE: + IoDeleteDevice( device_obj ); + case IRP_MN_SURPRISE_REMOVAL: + irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest( irp, IO_NO_INCREMENT ); + return STATUS_SUCCESS; + default: + FIXME( "Unhandled minor function %s.\n", debugstr_minor_function_code( stack->MinorFunction ) ); + break; + } + + irp->IoStatus.Status = ret; + IoCompleteRequest( irp, IO_NO_INCREMENT ); + return ret; +} + +static NTSTATUS WINAPI bluetooth_pnp( DEVICE_OBJECT *device, IRP *irp ) +{ + if (device == bus_fdo) + return fdo_pnp( device, irp ); + return pdo_pnp( device, irp ); +} + +static NTSTATUS WINAPI driver_add_device( DRIVER_OBJECT *driver, DEVICE_OBJECT *pdo ) +{ + NTSTATUS ret; + + TRACE( "(%p, %p)\n", driver, pdo ); + ret = IoCreateDevice( driver, 0, NULL, FILE_DEVICE_BUS_EXTENDER, 0, FALSE, &bus_fdo ); + if (ret != STATUS_SUCCESS) + { + ERR( "failed to create FDO: %#lx\n", ret ); + return ret; + } + + IoAttachDeviceToDeviceStack( bus_fdo, pdo ); + bus_pdo = pdo; + bus_fdo->Flags &= ~DO_DEVICE_INITIALIZING; + return STATUS_SUCCESS; +} + +static void WINAPI driver_unload( DRIVER_OBJECT *driver ) {} + +NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) +{ + TRACE( "(%p, %s)\n", driver, debugstr_w( path->Buffer ) ); + + driver->DriverExtension->AddDevice = driver_add_device; + driver_obj = driver; + + driver->MajorFunction[IRP_MJ_PNP] = bluetooth_pnp; + driver->DriverUnload = driver_unload; + + return STATUS_SUCCESS; +} diff --git a/dlls/winebth.sys/winebth.inf b/dlls/winebth.sys/winebth.inf new file mode 100644 index 00000000000..6b3b8be0ed0 --- /dev/null +++ b/dlls/winebth.sys/winebth.inf @@ -0,0 +1,22 @@ +[Version] +Signature="$CHICAGO$" +ClassGuid={4d36e97d-e325-11ce-bfc1-08002be10318} +Class=System + +[Manufacturer] +Wine=mfg_section + +[mfg_section] +Wine Bluetooth bus driver=device_section,root\winebth + +[device_section.Services] +AddService = winebth,0x2,svc_section + +[svc_section] +Description="Wine Bluetooth bus driver" +DisplayName="Wine Bluetooth bus" +ServiceBinary="%12%\winebth.sys" +LoadOrderGroup="WinePlugPlay" +ServiceType=1 +StartType=3 +ErrorControl=1 diff --git a/dlls/winebth.sys/winebth.rc b/dlls/winebth.sys/winebth.rc new file mode 100644 index 00000000000..33157042b7c --- /dev/null +++ b/dlls/winebth.sys/winebth.rc @@ -0,0 +1,20 @@ +/* + * Copyright 2024 Vibhav Pant + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* @makedep: winebth.inf */ +1 WINE_DATA_FILE winebth.inf diff --git a/dlls/winebth.sys/winebth_priv.h b/dlls/winebth.sys/winebth_priv.h new file mode 100644 index 00000000000..25d69dd2386 --- /dev/null +++ b/dlls/winebth.sys/winebth_priv.h @@ -0,0 +1,74 @@ +/* + * Private winebth.sys defs + * + * Copyright 2024 Vibhav Pant + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __WINE_WINEBTH_WINEBTH_H_ +#define __WINE_WINEBTH_WINEBTH_H_ + +#ifdef __ASM_USE_FASTCALL_WRAPPER +extern void * WINAPI wrap_fastcall_func1(void *func, const void *a); +__ASM_STDCALL_FUNC(wrap_fastcall_func1, 8, + "popl %ecx\n\t" + "popl %eax\n\t" + "xchgl (%esp),%ecx\n\t" + "jmp *%eax"); +#define call_fastcall_func1(func,a) wrap_fastcall_func1(func,a) +#else +#define call_fastcall_func1(func,a) func(a) +#endif + +#define XX(i) case (i): return #i + + +static inline const char *debugstr_minor_function_code( UCHAR code ) +{ + switch(code) + { + XX(IRP_MN_START_DEVICE); + XX(IRP_MN_QUERY_REMOVE_DEVICE); + XX(IRP_MN_REMOVE_DEVICE); + XX(IRP_MN_CANCEL_REMOVE_DEVICE); + XX(IRP_MN_STOP_DEVICE); + XX(IRP_MN_QUERY_STOP_DEVICE); + XX(IRP_MN_CANCEL_STOP_DEVICE); + XX(IRP_MN_QUERY_DEVICE_RELATIONS); + XX(IRP_MN_QUERY_INTERFACE); + XX(IRP_MN_QUERY_CAPABILITIES); + XX(IRP_MN_QUERY_RESOURCES); + XX(IRP_MN_QUERY_RESOURCE_REQUIREMENTS); + XX(IRP_MN_QUERY_DEVICE_TEXT); + XX(IRP_MN_FILTER_RESOURCE_REQUIREMENTS); + XX(IRP_MN_READ_CONFIG); + XX(IRP_MN_WRITE_CONFIG); + XX(IRP_MN_EJECT); + XX(IRP_MN_SET_LOCK); + XX(IRP_MN_QUERY_ID); + XX(IRP_MN_QUERY_PNP_DEVICE_STATE); + XX(IRP_MN_QUERY_BUS_INFORMATION); + XX(IRP_MN_DEVICE_USAGE_NOTIFICATION); + XX(IRP_MN_SURPRISE_REMOVAL); + XX(IRP_MN_QUERY_LEGACY_BUS_INFORMATION); + default: + return wine_dbg_sprintf( "(unknown %#x)", code ); + } +} +#undef XX + + +#endif /* __WINE_WINEBTH_WINEBTH_H_ */ diff --git a/loader/wine.inf.in b/loader/wine.inf.in index 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..12d1b4ddcc9 100644 --- a/dlls/winebth.sys/Makefile.in +++ b/dlls/winebth.sys/Makefile.in @@ -1,8 +1,13 @@ MODULE = winebth.sys -IMPORTS = ntoskrnl +IMPORTS = ntoskrnl uuid +UNIXLIB = winebth.so +UNIX_CFLAGS = $(DBUS_CFLAGS)
EXTRADLLFLAGS = -Wl,--subsystem,native
SOURCES = \ + dbus.c \ + unixlib.c \ + winebluetooth.c \ winebth.c \ winebth.rc diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c new file mode 100644 index 00000000000..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 27808483e02..fa7f9f25677 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 ); @@ -137,13 +141,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 | 232 +++++++++++++++++++++++++++++ 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 | 60 ++++++++ dlls/winebth.sys/winebth.c | 248 ++++++++++++++++++++++++++++++- dlls/winebth.sys/winebth_priv.h | 14 ++ 8 files changed, 747 insertions(+), 4 deletions(-)
diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index 004d3ac42ad..421091e683d 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,8 @@ WINE_DECLARE_DEBUG_CHANNEL( dbus );
const int bluez_timeout = -1;
+#define DBUS_INTERFACE_OBJECTMANAGER "org.freedesktop.DBus.ObjectManager" + #define BLUEZ_DEST "org.bluez" #define BLUEZ_INTERFACE_ADAPTER "org.bluez.Adapter1"
@@ -79,12 +87,75 @@ failed: return FALSE; }
+static const char *bluez_next_dict_entry( DBusMessageIter *iter, DBusMessageIter *variant ) +{ + DBusMessageIter sub; + const char *name; + + if (p_dbus_message_iter_get_arg_type( iter ) != DBUS_TYPE_DICT_ENTRY) + return NULL; + + p_dbus_message_iter_recurse( iter, &sub ); + p_dbus_message_iter_next( iter ); + p_dbus_message_iter_get_basic( &sub, &name ); + p_dbus_message_iter_next( &sub ); + p_dbus_message_iter_recurse( &sub, variant ); + return name; +} + static inline const char *dbgstr_dbus_connection( DBusConnection *connection ) { return wine_dbg_sprintf( "{%p connected=%d}", connection, p_dbus_connection_get_is_connected( connection ) ); }
+static NTSTATUS bluez_get_objects_async( DBusConnection *connection, DBusPendingCall **call ) +{ + DBusMessage *request; + dbus_bool_t success; + + TRACE( "Getting managed objects under '/' at service '%s'\n", BLUEZ_DEST ); + request = p_dbus_message_new_method_call( + BLUEZ_DEST, "/", DBUS_INTERFACE_OBJECTMANAGER, "GetManagedObjects" ); + if (!request) + { + return STATUS_NO_MEMORY; + } + + success = p_dbus_connection_send_with_reply( connection, request, call, -1 ); + p_dbus_message_unref( request ); + if (!success) + return STATUS_NO_MEMORY; + + if (*call == NULL) + return STATUS_INVALID_PARAMETER; + + return STATUS_SUCCESS; +} + +#define DBUS_OBJECTMANAGER_METHOD_GETMANAGEDOBJECTS_RETURN_SIGNATURE \ + DBUS_TYPE_ARRAY_AS_STRING \ + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING \ + DBUS_TYPE_OBJECT_PATH_AS_STRING \ + DBUS_TYPE_ARRAY_AS_STRING \ + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING \ + DBUS_TYPE_STRING_AS_STRING \ + DBUS_TYPE_ARRAY_AS_STRING \ + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING \ + DBUS_TYPE_STRING_AS_STRING \ + DBUS_TYPE_VARIANT_AS_STRING \ + DBUS_DICT_ENTRY_END_CHAR_AS_STRING \ + DBUS_DICT_ENTRY_END_CHAR_AS_STRING \ + DBUS_DICT_ENTRY_END_CHAR_AS_STRING + +struct bluez_watcher_ctx +{ + void *init_device_list_call; + + /* struct bluez_init_entry */ + struct list initial_radio_list; +}; + void *bluez_dbus_init( void ) { DBusError error; @@ -121,6 +192,167 @@ void bluez_dbus_free( void *connection )
p_dbus_connection_unref( connection ); } + +struct bluez_watcher_event +{ + struct list entry; + enum winebluetooth_watcher_event_type event_type; + union winebluetooth_watcher_event_data event; +}; + +NTSTATUS bluez_watcher_init( void *connection, void **ctx ) +{ + NTSTATUS status; + DBusPendingCall *call; + struct bluez_watcher_ctx *watcher_ctx = + calloc( 1, sizeof( struct bluez_watcher_ctx ) ); + + if (watcher_ctx == NULL) return STATUS_NO_MEMORY; + status = bluez_get_objects_async( connection, &call ); + if (status != STATUS_SUCCESS) + { + free( watcher_ctx ); + ERR( "could not create async GetManagedObjects call: %#x\n", (int)status); + return status; + } + watcher_ctx->init_device_list_call = call; + list_init( &watcher_ctx->initial_radio_list ); + + *ctx = watcher_ctx; + TRACE( "ctx=%p\n", ctx ); + return STATUS_SUCCESS; +} + +struct bluez_init_entry +{ + union { + struct winebluetooth_watcher_event_radio_added radio; + } object; + struct list entry; +}; + +static NTSTATUS bluez_build_initial_device_lists( DBusMessage *reply, struct list *adapter_list ) +{ + DBusMessageIter dict, paths_iter, iface_iter, prop_iter; + const char *path; + NTSTATUS status = STATUS_SUCCESS; + + if (!p_dbus_message_has_signature( reply, + DBUS_OBJECTMANAGER_METHOD_GETMANAGEDOBJECTS_RETURN_SIGNATURE )) + { + ERR( "Unexpected signature in GetManagedObjects reply: %s\n", p_dbus_message_get_signature( reply ) ); + return STATUS_INTERNAL_ERROR; + } + + p_dbus_message_iter_init( reply, &dict ); + p_dbus_message_iter_recurse( &dict, &paths_iter ); + while((path = bluez_next_dict_entry( &paths_iter, &iface_iter ))) + { + const char *iface; + while ((iface = bluez_next_dict_entry ( &iface_iter, &prop_iter ))) + { + if (!strcmp( iface, BLUEZ_INTERFACE_ADAPTER )) + { + struct bluez_init_entry *init_device = calloc( 1, sizeof( *init_device ) ); + struct unix_name *radio_name; + + if (!init_device) + { + status = STATUS_NO_MEMORY; + goto done; + } + radio_name = unix_name_get_or_create( path ); + if (!radio_name) + { + free( init_device ); + status = STATUS_NO_MEMORY; + goto done; + } + init_device->object.radio.radio.handle = (UINT_PTR)radio_name; + list_add_tail( adapter_list, &init_device->entry ); + TRACE( "Found BlueZ org.bluez.Adapter1 object %s: %p\n", + debugstr_a( radio_name->str ), radio_name ); + break; + } + } + } + + TRACE( "Initial device list: radios: %d\n", list_count( adapter_list ) ); + done: + return status; +} + +static struct bluez_init_entry *bluez_init_entries_list_pop( struct list *list ) +{ + struct list *entry = list_head( list ); + struct bluez_init_entry *device = LIST_ENTRY( entry, struct bluez_init_entry, entry ); + + list_remove( entry ); + return device; +} + +NTSTATUS bluez_dbus_loop( void *c, void *watcher, + struct winebluetooth_event *result ) +{ + DBusConnection *connection; + struct bluez_watcher_ctx *watcher_ctx = watcher; + + TRACE( "(%p, %p, %p)\n", c, watcher, result ); + connection = p_dbus_connection_ref( c ); + + while (TRUE) + { + if (!list_empty( &watcher_ctx->initial_radio_list )) + { + struct bluez_init_entry *radio = + bluez_init_entries_list_pop( &watcher_ctx->initial_radio_list ); + struct winebluetooth_watcher_event *watcher_event = &result->data.watcher_event; + + result->status = WINEBLUETOOTH_EVENT_WATCHER_EVENT; + watcher_event->event_type = BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED; + watcher_event->event_data.radio_added = radio->object.radio; + free( radio ); + p_dbus_connection_unref( connection ); + return STATUS_PENDING; + } + else if (!p_dbus_connection_read_write_dispatch( connection, -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 ); + 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..826c180ea7c 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_get_event( void *args ) +{ + struct bluetooth_get_event_params *params = args; + + if (!dbus_connection) return STATUS_NOT_SUPPORTED; + memset( ¶ms->result, 0, sizeof( params->result ) ); + return bluez_dbus_loop( dbus_connection, 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_get_event, };
C_ASSERT( ARRAYSIZE( __wine_unix_call_funcs ) == unix_funcs_count ); diff --git a/dlls/winebth.sys/unixlib.h b/dlls/winebth.sys/unixlib.h index e79a2c4745a..4b6354a96e2 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_get_event_params +{ + unix_handle_t unix_watcher_ctx; + + struct winebluetooth_event result; +}; + enum bluetoothapis_funcs { unix_bluetooth_init, unix_bluetooth_shutdown,
+ unix_bluetooth_adapter_free, + + unix_bluetooth_watcher_init, + unix_bluetooth_watcher_close, + + unix_bluetooth_get_event, + unix_funcs_count };
diff --git a/dlls/winebth.sys/unixlib_priv.h b/dlls/winebth.sys/unixlib_priv.h index 73b3b20aad8..1834aaf3a59 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 *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..d29b4f1f2b1 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_get_event( winebluetooth_watcher_t watcher, + struct winebluetooth_event *result ) +{ + struct bluetooth_get_event_params params = {0}; + NTSTATUS status; + + TRACE( "(%p, %p)\n", (void *)watcher.handle, result ); + + params.unix_watcher_ctx = watcher.handle; + status = UNIX_BLUETOOTH_CALL( bluetooth_get_event, ¶ms ); + *result = params.result; + return status; +} + NTSTATUS winebluetooth_init( void ) { NTSTATUS status; diff --git a/dlls/winebth.sys/winebluetooth.h b/dlls/winebth.sys/winebluetooth.h index b22175e22a5..4ecd3d31c64 100644 --- a/dlls/winebth.sys/winebluetooth.h +++ b/dlls/winebth.sys/winebluetooth.h @@ -22,6 +22,66 @@ #ifndef __WINE_WINEBLUETOOTH_H__ #define __WINE_WINEBLUETOOTH_H__
+#include <bthsdpdef.h> +#include <bluetoothapis.h> + +typedef struct +{ + UINT_PTR handle; +} winebluetooth_radio_t; + +struct winebluetooth_bluetooth_version +{ + UCHAR major; + UCHAR minor; +}; + +void winebluetooth_radio_free( winebluetooth_radio_t radio ); + +typedef struct +{ + UINT_PTR handle; +} winebluetooth_watcher_t; + +enum winebluetooth_watcher_event_type +{ + BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED, +}; + +struct winebluetooth_watcher_event_radio_added +{ + winebluetooth_radio_t radio; +}; + +union winebluetooth_watcher_event_data +{ + struct winebluetooth_watcher_event_radio_added radio_added; +}; + +struct winebluetooth_watcher_event +{ + enum winebluetooth_watcher_event_type event_type; + union winebluetooth_watcher_event_data event_data; +}; + +NTSTATUS winebluetooth_watcher_init( winebluetooth_watcher_t *watcher ); +NTSTATUS winebluetooth_watcher_close( winebluetooth_watcher_t watcher ); + +enum winebluetooth_event_type +{ + WINEBLUETOOTH_EVENT_WATCHER_EVENT, +}; + +struct winebluetooth_event +{ + enum winebluetooth_event_type status; + union { + struct winebluetooth_watcher_event watcher_event; + } data; +}; + +NTSTATUS winebluetooth_get_event( winebluetooth_watcher_t watcher, + struct winebluetooth_event *result ); NTSTATUS winebluetooth_init( void ); NTSTATUS winebluetooth_shutdown( void );
diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index fa7f9f25677..7e528c15bdb 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 result = {0}; + + status = winebluetooth_get_event( watcher, &result ); + if (status != STATUS_PENDING) break; + + switch (result.status) + { + case WINEBLUETOOTH_EVENT_WATCHER_EVENT: + { + struct winebluetooth_watcher_event *event = &result.data.watcher_event; + switch (event->event_type) + { + case BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED: + add_bluetooth_radio( event->event_data.radio_added ); + break; + default: + FIXME( "Unknown bluetooth watcher event code: %#x\n", event->event_type ); + } + break; + } + default: + FIXME( "Unknown bluetooth event loop status code: %#x\n", result.status ); + } + } + + if (status != STATUS_SUCCESS) + ERR( "Bluetooth event loop terminated with %#lx", status ); + else + TRACE( "Exiting bluetooth event loop\n" ); + 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,42 @@ 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"" ); + 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,11 +318,23 @@ static NTSTATUS WINAPI pdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) ret = STATUS_SUCCESS; break; case IRP_MN_REMOVE_DEVICE: - IoDeleteDevice( device_obj ); + assert( device->removed ); + remove_pending_irps( device ); + winebluetooth_radio_free( device->radio ); + IoDeleteDevice( device->device_obj ); + ret = STATUS_SUCCESS; + break; case IRP_MN_SURPRISE_REMOVAL: - irp->IoStatus.Status = STATUS_SUCCESS; - IoCompleteRequest( irp, IO_NO_INCREMENT ); - return STATUS_SUCCESS; + 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: FIXME( "Unhandled minor function %s.\n", debugstr_minor_function_code( stack->MinorFunction ) ); break; @@ -148,6 +381,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 | 31 +++++++++++++++++- 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(+), 1 deletion(-)
diff --git a/dlls/winebth.sys/unixlib.c b/dlls/winebth.sys/unixlib.c index 826c180ea7c..2a3607046dd 100644 --- a/dlls/winebth.sys/unixlib.c +++ b/dlls/winebth.sys/unixlib.c @@ -27,7 +27,6 @@ #include <stdlib.h> #include <stdarg.h> #include <assert.h> -#include <ctype.h> #include <pthread.h>
#include <ntstatus.h> @@ -110,6 +109,35 @@ static NTSTATUS bluetooth_shutdown( void *params ) return STATUS_SUCCESS; }
+static NTSTATUS get_unique_name( const struct unix_name *name, char *buf, SIZE_T *buf_size ) +{ + SIZE_T path_len, i; + + path_len = strlen( name->str ); + if (*buf_size <= (path_len * sizeof(char))) + { + *buf_size = (path_len + 1) * sizeof(char); + return STATUS_BUFFER_TOO_SMALL; + } + + for (i = 0; i < path_len; i++) + { + if (name->str[i] == '/') buf[i] = '_'; + else + buf[i] = name->str[i]; + } + buf[path_len] = '\0'; + return STATUS_SUCCESS; +} + +static NTSTATUS bluetooth_adapter_get_unique_name( void *args ) +{ + struct bluetooth_adapter_get_unique_name_params *params = args; + if (!dbus_connection) return STATUS_NOT_SUPPORTED; + + return get_unique_name( params->adapter, params->buf, ¶ms->buf_size ); +} + static NTSTATUS bluetooth_adapter_free( void *args ) { struct bluetooth_adapter_free_params *params = args; @@ -144,6 +172,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 4b6354a96e2..c3b601715db 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 d29b4f1f2b1..57e9b1b9eac 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 4ecd3d31c64..f4de9594247 100644 --- a/dlls/winebth.sys/winebluetooth.h +++ b/dlls/winebth.sys/winebluetooth.h @@ -36,6 +36,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 7e528c15bdb..c8f50541717 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"" ); break; @@ -320,6 +373,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 ); ret = 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 c8f50541717..31fed12c175 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; };
@@ -368,11 +372,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
On Thu Oct 17 03:05:16 2024 +0000, Elizabeth Figura wrote:
We now have "winebluetooth.h" and "winebth_priv.h". What's the difference?
`winebluetooth(.h)` is just a thin wrapper around the unixlib. `winebth_priv.h` is driver-related code, which also contains common declarations needed for the `bthenum` minidriver in later code.