The Windows Bluetooth stack uses private IOCTLs in order to set the [discoverability](https://learn.microsoft.com/en-us/windows/win32/api/bluetoothapis/nf-bluetoo...) and [connectability](https://learn.microsoft.com/en-us/windows/win32/api/bluetoothapis/nf-bluetoo...) status of a local radio. This MR introduces the wine-specific IOCTL `IOCTL_WINBTH_RADIO_SET_FLAG` for the same purpose, as most applications seem to the Win32 API itself for these operations.
-- v16: bluetoothapis: Implement BluetoothEnableDiscovery. bluetoothapis/tests: Add tests for BluetoothEnableDiscovery. bluetoothapis: Add stub for BluetoothEnableDiscovery. bluetoothapis: Implement BluetoothEnableIncomingConnections. bluetoothapis/tests: Add tests for BluetoothEnableIncomingConnections. bluetoothapis: Add stub for BluetoothEnableIncomingConnections. winebth.sys: Call bluez_watcher_close as part of bluetooth_shutdown. winebth.sys: Implement IOCTL_WINEBTH_RADIO_SET_FLAG.
From: Vibhav Pant vibhavp@gmail.com
--- dlls/winebth.sys/Makefile.in | 1 + dlls/winebth.sys/dbus.c | 151 +++++++++++++++++++++++++++++++ dlls/winebth.sys/dbus.h | 2 + dlls/winebth.sys/unixlib.c | 10 ++ dlls/winebth.sys/unixlib.h | 10 ++ dlls/winebth.sys/unixlib_priv.h | 3 + dlls/winebth.sys/winebluetooth.c | 15 +++ dlls/winebth.sys/winebth.c | 24 ++++- dlls/winebth.sys/winebth_priv.h | 16 +++- include/Makefile.in | 1 + include/wine/winebth.h | 42 +++++++++ 11 files changed, 270 insertions(+), 5 deletions(-) create mode 100644 include/wine/winebth.h
diff --git a/dlls/winebth.sys/Makefile.in b/dlls/winebth.sys/Makefile.in index 12d1b4ddcc9..b09cfe40aa7 100644 --- a/dlls/winebth.sys/Makefile.in +++ b/dlls/winebth.sys/Makefile.in @@ -2,6 +2,7 @@ MODULE = winebth.sys IMPORTS = ntoskrnl uuid UNIXLIB = winebth.so UNIX_CFLAGS = $(DBUS_CFLAGS) +UNIX_LIBS = $(PTHREAD_LIBS)
EXTRADLLFLAGS = -Wl,--subsystem,native
diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index 1cd6870e7de..4a475024be8 100644 --- a/dlls/winebth.sys/dbus.c +++ b/dlls/winebth.sys/dbus.c @@ -2,6 +2,7 @@ * Support for communicating with BlueZ over DBus. * * Copyright 2024 Vibhav Pant + * Copyright 2025 Vibhav Pant * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -29,6 +30,9 @@ #include <dlfcn.h> #include <assert.h> #include <pthread.h> +#include <semaphore.h> +#include <errno.h> +#include <string.h>
#ifdef SONAME_LIBDBUS_1 #include <dbus/dbus.h> @@ -41,6 +45,7 @@ #include <winbase.h> #include <bthsdpdef.h> #include <bluetoothapis.h> +#include <wine/winebth.h>
#include <wine/debug.h>
@@ -264,6 +269,148 @@ static void parse_mac_address( const char *addr_str, BYTE dest[6] ) dest[i] = addr[i]; }
+static void bluez_dbus_wait_for_reply_callback( DBusPendingCall *pending_call, void *wait ) +{ + sem_post( wait ); +} + +static NTSTATUS bluez_dbus_pending_call_wait( DBusPendingCall *pending_call, DBusMessage **reply, DBusError *error ) +{ + sem_t wait; + + sem_init( &wait, 0, 0 ); + if (!p_dbus_pending_call_set_notify( pending_call, bluez_dbus_wait_for_reply_callback, &wait, NULL )) + { + sem_destroy( &wait ); + p_dbus_pending_call_unref( pending_call ); + return STATUS_NO_MEMORY; + } + for (;;) + { + int ret = sem_wait( &wait ); + if (!ret) + { + *reply = p_dbus_pending_call_steal_reply( pending_call ); + if (p_dbus_set_error_from_message( error, *reply )) + { + p_dbus_message_unref( *reply ); + *reply = NULL; + } + p_dbus_pending_call_unref( pending_call ); + sem_destroy( &wait ); + return STATUS_SUCCESS; + } + if (errno == EINTR) + continue; + + ERR( "Failed to wait for DBus method reply: %s\n", debugstr_a( strerror( errno ) ) ); + sem_destroy( &wait ); + p_dbus_pending_call_cancel( pending_call ); + p_dbus_pending_call_unref( pending_call ); + return STATUS_INTERNAL_ERROR; + } +} + +/* Like dbus_connection_send_with_reply_and_block, but it does not acquire a lock on the connection, instead relying on + * the main loop in bluez_dbus_loop. This is faster than send_with_reply_and_block. + * This takes ownership of the request, so there is no need to unref it. */ +static NTSTATUS bluez_dbus_send_and_wait_for_reply( DBusConnection *connection, DBusMessage *request, DBusMessage **reply, + DBusError *error ) +{ + DBusPendingCall *pending_call; + dbus_bool_t success; + + success = p_dbus_connection_send_with_reply( connection, request, &pending_call, bluez_timeout ); + p_dbus_message_unref( request ); + + if (!success) + return STATUS_NO_MEMORY; + return bluez_dbus_pending_call_wait( pending_call, reply, error ); +} + +NTSTATUS bluez_adapter_set_prop( void *connection, struct bluetooth_adapter_set_prop_params *params ) +{ + DBusMessage *request, *reply; + DBusMessageIter iter, sub_iter; + DBusError error; + DBusBasicValue val; + int val_type; + static const char *adapter_iface = BLUEZ_INTERFACE_ADAPTER; + const char *prop_name; + NTSTATUS status; + + TRACE( "(%p, %p)\n", connection, params ); + + switch (params->prop_flag) + { + case LOCAL_RADIO_CONNECTABLE: + prop_name = "Discoverable"; + val.bool_val = params->prop->boolean; + val_type = DBUS_TYPE_BOOLEAN; + break; + case LOCAL_RADIO_DISCOVERABLE: + prop_name = "Connectable"; + val.bool_val = params->prop->boolean; + val_type = DBUS_TYPE_BOOLEAN; + break; + default: + return STATUS_INVALID_PARAMETER; + } + + TRACE( "Setting property %s for adapter %s\n", debugstr_a( prop_name ), debugstr_a( params->adapter->str ) ); + request = p_dbus_message_new_method_call( BLUEZ_DEST, params->adapter->str, + DBUS_INTERFACE_PROPERTIES, "Set" ); + if (!request) return STATUS_NO_MEMORY; + + p_dbus_message_iter_init_append( request, &iter ); + if (!p_dbus_message_iter_append_basic( &iter, DBUS_TYPE_STRING, &adapter_iface )) + { + p_dbus_message_unref( request ); + return STATUS_NO_MEMORY; + } + if (!p_dbus_message_iter_append_basic( &iter, DBUS_TYPE_STRING, &prop_name )) + { + p_dbus_message_unref( request ); + return STATUS_NO_MEMORY; + } + if (!p_dbus_message_iter_open_container( &iter, DBUS_TYPE_VARIANT, DBUS_TYPE_BOOLEAN_AS_STRING, &sub_iter )) + { + p_dbus_message_unref( request ); + return STATUS_NO_MEMORY; + } + if (!p_dbus_message_iter_append_basic( &sub_iter, val_type, &val )) + { + p_dbus_message_iter_abandon_container( &iter, &sub_iter ); + p_dbus_message_unref( request ); + return STATUS_NO_MEMORY; + } + if (!p_dbus_message_iter_close_container( &iter, &sub_iter )) + { + p_dbus_message_unref( request ); + return STATUS_NO_MEMORY; + } + + p_dbus_error_init( &error ); + status = bluez_dbus_send_and_wait_for_reply( connection, request, &reply, &error ); + if (status) + { + p_dbus_error_free( &error ); + return status; + } + if (!reply) + { + ERR( "Failed to set property %s for adapter %s: %s: %s\n", debugstr_a( prop_name ), + debugstr_a( params->adapter->str ), debugstr_a( error.name ), debugstr_a( error.message ) ); + status = bluez_dbus_error_to_ntstatus( &error ); + p_dbus_error_free( &error ); + return status; + } + p_dbus_error_free( &error ); + p_dbus_message_unref( reply ); + + return STATUS_SUCCESS; +} + 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, @@ -977,5 +1124,9 @@ NTSTATUS bluez_dbus_loop( void *c, void *watcher, struct winebluetooth_event *re { return STATUS_NOT_SUPPORTED; } +NTSTATUS bluez_adapter_set_prop( void *connection, struct bluetooth_adapter_set_prop_params *params ) +{ + return STATUS_NOT_SUPPORTED; +}
#endif /* SONAME_LIBDBUS_1 */ diff --git a/dlls/winebth.sys/dbus.h b/dlls/winebth.sys/dbus.h index 025d2c6fd2c..19b94776fd3 100644 --- a/dlls/winebth.sys/dbus.h +++ b/dlls/winebth.sys/dbus.h @@ -2,6 +2,7 @@ * DBus declarations. * * Copyright 2024 Vibhav Pant + * Copyright 2025 Vibhav Pant * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -79,6 +80,7 @@ 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_abandon_container); \ DO_FUNC(dbus_message_iter_close_container); \ DO_FUNC(dbus_message_iter_get_arg_type); \ DO_FUNC(dbus_message_iter_get_element_type); \ diff --git a/dlls/winebth.sys/unixlib.c b/dlls/winebth.sys/unixlib.c index 09641be6396..982652498e5 100644 --- a/dlls/winebth.sys/unixlib.c +++ b/dlls/winebth.sys/unixlib.c @@ -2,6 +2,7 @@ * winebluetooth Unix interface * * Copyright 2024 Vibhav Pant + * Copyright 2025 Vibhav Pant * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -154,6 +155,14 @@ static NTSTATUS bluetooth_adapter_free( void *args ) return STATUS_SUCCESS; }
+static NTSTATUS bluetooth_adapter_set_prop( void *arg ) +{ + struct bluetooth_adapter_set_prop_params *params = arg; + + if (!dbus_connection) return STATUS_NOT_SUPPORTED; + return bluez_adapter_set_prop( dbus_connection, params ); +} + static NTSTATUS bluetooth_get_event( void *args ) { struct bluetooth_get_event_params *params = args; @@ -167,6 +176,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = { bluetooth_init, bluetooth_shutdown,
+ bluetooth_adapter_set_prop, bluetooth_adapter_get_unique_name, bluetooth_adapter_free,
diff --git a/dlls/winebth.sys/unixlib.h b/dlls/winebth.sys/unixlib.h index 45a000a2e88..f1530bcde19 100644 --- a/dlls/winebth.sys/unixlib.h +++ b/dlls/winebth.sys/unixlib.h @@ -2,6 +2,7 @@ * Unix interface definitions * * Copyright 2024 Vibhav Pant + * Copyright 2025 Vibhav Pant * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -56,6 +57,14 @@ struct bluetooth_adapter_get_unique_name_params SIZE_T buf_size; };
+struct bluetooth_adapter_set_prop_params +{ + unix_name_t adapter; + ULONG prop_flag; + + union winebluetooth_property *prop; +}; + struct bluetooth_get_event_params { struct winebluetooth_event result; @@ -66,6 +75,7 @@ enum bluetoothapis_funcs unix_bluetooth_init, unix_bluetooth_shutdown,
+ unix_bluetooth_adapter_set_prop, unix_bluetooth_adapter_get_unique_name, unix_bluetooth_adapter_free,
diff --git a/dlls/winebth.sys/unixlib_priv.h b/dlls/winebth.sys/unixlib_priv.h index 5ed5204e930..5146799e705 100644 --- a/dlls/winebth.sys/unixlib_priv.h +++ b/dlls/winebth.sys/unixlib_priv.h @@ -2,6 +2,7 @@ * Bluetoothapis Unix interface * * Copyright 2024 Vibhav Pant + * Copyright 2025 Vibhav Pant * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -47,6 +48,8 @@ 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_adapter_set_prop( void *connection, + struct bluetooth_adapter_set_prop_params *params ); extern NTSTATUS bluez_watcher_init( void *connection, void **ctx ); extern void bluez_watcher_close( void *connection, void *ctx ); #endif /* __WINE_WINEBTH_UNIXLIB_PRIV_H */ diff --git a/dlls/winebth.sys/winebluetooth.c b/dlls/winebth.sys/winebluetooth.c index 3d5aa77094d..a477e00fc08 100644 --- a/dlls/winebth.sys/winebluetooth.c +++ b/dlls/winebth.sys/winebluetooth.c @@ -2,6 +2,7 @@ * Wine bluetooth APIs * * Copyright 2024 Vibhav Pant + * Copyright 2025 Vibhav Pant * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -53,6 +54,20 @@ NTSTATUS winebluetooth_radio_get_unique_name( winebluetooth_radio_t radio, char return status; }
+NTSTATUS winebluetooth_radio_set_property( winebluetooth_radio_t radio, + ULONG prop_flag, + union winebluetooth_property *property ) +{ + struct bluetooth_adapter_set_prop_params params = { 0 }; + + TRACE( "(%p, %#lx, %p)\n", (void *)radio.handle, prop_flag, property ); + + params.adapter = radio.handle; + params.prop_flag = prop_flag; + params.prop = property; + return UNIX_BLUETOOTH_CALL( bluetooth_adapter_set_prop, ¶ms ); +} + void winebluetooth_radio_free( winebluetooth_radio_t radio ) { struct bluetooth_adapter_free_params args = { 0 }; diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index 5ba846e6459..5bdea771d6f 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -1,7 +1,7 @@ /* * Bluetooth bus driver * - * Copyright 2024 Vibhav Pant + * Copyright 2024-2025 Vibhav Pant * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -37,6 +37,7 @@ #include <bthioctl.h> #include <ddk/wdm.h>
+#include <wine/winebth.h> #include <wine/debug.h> #include <wine/list.h>
@@ -84,6 +85,7 @@ static NTSTATUS WINAPI dispatch_bluetooth( DEVICE_OBJECT *device, IRP *irp ) struct bluetooth_radio *ext = (struct bluetooth_radio *)device->DeviceExtension; IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp ); ULONG code = stack->Parameters.DeviceIoControl.IoControlCode; + ULONG insize = stack->Parameters.DeviceIoControl.InputBufferLength; ULONG outsize = stack->Parameters.DeviceIoControl.OutputBufferLength; NTSTATUS status = irp->IoStatus.Status;
@@ -133,6 +135,26 @@ static NTSTATUS WINAPI dispatch_bluetooth( DEVICE_OBJECT *device, IRP *irp ) status = STATUS_SUCCESS; break; } + case IOCTL_WINEBTH_RADIO_SET_FLAG: + { + const struct winebth_radio_set_flag_params *params = irp->AssociatedIrp.SystemBuffer; + union winebluetooth_property prop_value = {0}; + + if (!params) + { + status = STATUS_INVALID_PARAMETER; + break; + } + if (insize < sizeof( *params )) + { + status = STATUS_BUFFER_TOO_SMALL; + break; + } + + prop_value.boolean = !!params->enable; + status = winebluetooth_radio_set_property( ext->radio, params->flag, &prop_value ); + break; + } default: FIXME( "Unimplemented IOCTL code: %#lx\n", code ); break; diff --git a/dlls/winebth.sys/winebth_priv.h b/dlls/winebth.sys/winebth_priv.h index 117b02f3294..79ef0bd457a 100644 --- a/dlls/winebth.sys/winebth_priv.h +++ b/dlls/winebth.sys/winebth_priv.h @@ -2,6 +2,7 @@ * Private winebth.sys defs * * Copyright 2024 Vibhav Pant + * Copyright 2025 Vibhav Pant * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -108,10 +109,6 @@ DEFINE_BTH_RADIO_DEVPROPKEY( HCIVendorFeatures, 8 ); /* DEVPROP_TY DEFINE_BTH_RADIO_DEVPROPKEY( MaximumAdvertisementDataLength, 17 ); /* DEVPROP_TYPE_UINT16 */ DEFINE_BTH_RADIO_DEVPROPKEY( LELocalSupportedFeatures, 22 ); /* DEVPROP_TYPE_UINT64 */
-/* Valid masks for the "flags" field in BTH_LOCAL_RADIO_INFO. */ -#define LOCAL_RADIO_DISCOVERABLE 0x0001 -#define LOCAL_RADIO_CONNECTABLE 0x0002 - typedef struct { UINT_PTR handle; @@ -136,6 +133,14 @@ typedef UINT16 winebluetooth_radio_props_mask_t; WINEBLUETOOTH_RADIO_PROPERTY_VERSION | WINEBLUETOOTH_RADIO_PROPERTY_DISCOVERING | \ WINEBLUETOOTH_RADIO_PROPERTY_PAIRABLE)
+union winebluetooth_property +{ + BOOL boolean; + ULONG ulong; + BLUETOOTH_ADDRESS address; + WCHAR name[BLUETOOTH_MAX_NAME_SIZE]; +}; + struct winebluetooth_radio_properties { BOOL discoverable; @@ -156,6 +161,9 @@ static inline BOOL winebluetooth_radio_equal( winebluetooth_radio_t r1, wineblue { return r1.handle == r2.handle; } +NTSTATUS winebluetooth_radio_set_property( winebluetooth_radio_t radio, + ULONG prop_flag, + union winebluetooth_property *property );
enum winebluetooth_watcher_event_type { diff --git a/include/Makefile.in b/include/Makefile.in index cb2b83b6d8c..1886c623466 100644 --- a/include/Makefile.in +++ b/include/Makefile.in @@ -957,6 +957,7 @@ SOURCES = \ wine/wgl_driver.h \ wine/winbase16.h \ wine/windef16.h \ + wine/winebth.h \ wine/wine_common_ver.rc \ wine/wined3d.h \ wine/winedmo.h \ diff --git a/include/wine/winebth.h b/include/wine/winebth.h new file mode 100644 index 00000000000..c26a2100fb9 --- /dev/null +++ b/include/wine/winebth.h @@ -0,0 +1,42 @@ +/* + * Wine-specific IOCTL definitions for interfacing with winebth.sys + * + * Copyright 2025 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 __WINEBTH_H__ +#define __WINEBTH_H__ + +/* Set the discoverability or connectable flag for a local radio. Enabling discoverability will also enable incoming + * connections, while disabling incoming connections disables discoverability as well. */ +#define IOCTL_WINEBTH_RADIO_SET_FLAG CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xa3, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#include <pshpack1.h> + +#define LOCAL_RADIO_DISCOVERABLE 0x0001 +#define LOCAL_RADIO_CONNECTABLE 0x0002 + +struct winebth_radio_set_flag_params +{ + unsigned int flag: 2; + unsigned int enable : 1; +}; + +#include <poppack.h> + +#endif /* __WINEBTH_H__ */
From: Vibhav Pant vibhavp@gmail.com
This frees all associated resources with the watcher, including events and initial devices that were not consumed by the PE driver. --- dlls/winebth.sys/dbus.c | 63 ++++++++++++++++++++++++++++++++------ dlls/winebth.sys/unixlib.c | 1 + 2 files changed, 55 insertions(+), 9 deletions(-)
diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index 4a475024be8..1ab7ebaea74 100644 --- a/dlls/winebth.sys/dbus.c +++ b/dlls/winebth.sys/dbus.c @@ -525,6 +525,14 @@ struct bluez_watcher_ctx struct list event_list; };
+struct bluez_init_entry +{ + union { + struct winebluetooth_watcher_event_radio_added radio; + } object; + struct list entry; +}; + void *bluez_dbus_init( void ) { DBusError error; @@ -888,6 +896,46 @@ static const char BLUEZ_MATCH_PROPERTIES[] = "type='signal',"
static const char *BLUEZ_MATCH_RULES[] = { BLUEZ_MATCH_OBJECTMANAGER, BLUEZ_MATCH_PROPERTIES };
+/* Free up the watcher alongside any remaining events and initial devices and other associated resources. */ +static void bluez_watcher_free( struct bluez_watcher_ctx *watcher ) +{ + struct bluez_watcher_event *event1, *event2; + struct bluez_init_entry *entry1, *entry2; + + if (watcher->init_device_list_call) + { + p_dbus_pending_call_cancel( watcher->init_device_list_call ); + p_dbus_pending_call_unref( watcher->init_device_list_call ); + } + + LIST_FOR_EACH_ENTRY_SAFE( entry1, entry2, &watcher->initial_radio_list, struct bluez_init_entry, entry ) + { + list_remove( &entry1->entry ); + unix_name_free( (struct unix_name *)entry1->object.radio.radio.handle ); + free( entry1 ); + } + + LIST_FOR_EACH_ENTRY_SAFE( event1, event2, &watcher->event_list, struct bluez_watcher_event, entry ) + { + list_remove( &event1->entry ); + switch (event1->event_type) + { + case BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED: + unix_name_free( (struct unix_name *)event1->event.radio_added.radio.handle ); + break; + case BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_REMOVED: + unix_name_free( (struct unix_name *)event1->event.radio_removed.handle ); + break; + case BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_PROPERTIES_CHANGED: + unix_name_free( (struct unix_name *)event1->event.radio_props_changed.radio.handle ); + break; + } + free( event1 ); + } + + free( watcher ); +} + NTSTATUS bluez_watcher_init( void *connection, void **ctx ) { DBusError err; @@ -910,7 +958,10 @@ NTSTATUS bluez_watcher_init( void *connection, void **ctx )
list_init( &watcher_ctx->event_list );
- if (!p_dbus_connection_add_filter( connection, bluez_filter, watcher_ctx, free )) + /* The bluez_dbus_loop thread will free up the watcher when the disconnect message is processed (i.e, + * dbus_connection_read_write_dispatch returns false). Using a free-function with dbus_connection_add_filter + * is racy as the filter is removed from a different thread. */ + if (!p_dbus_connection_add_filter( connection, bluez_filter, watcher_ctx, NULL )) { p_dbus_pending_call_cancel( call ); p_dbus_pending_call_unref( call ); @@ -959,14 +1010,6 @@ void bluez_watcher_close( void *connection, void *ctx ) p_dbus_connection_remove_filter( connection, bluez_filter, ctx ); }
-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; @@ -1078,6 +1121,7 @@ NTSTATUS bluez_dbus_loop( void *c, void *watcher, } else if (!p_dbus_connection_read_write_dispatch( connection, 100 )) { + bluez_watcher_free( watcher_ctx ); p_dbus_connection_unref( connection ); TRACE( "Disconnected from DBus\n" ); return STATUS_SUCCESS; @@ -1120,6 +1164,7 @@ void *bluez_dbus_init( void ) { return NULL; } void bluez_dbus_close( void *connection ) {} void bluez_dbus_free( void *connection ) {} NTSTATUS bluez_watcher_init( void *connection, void **ctx ) { return STATUS_NOT_SUPPORTED; } +void bluez_watcher_close( void *connection, void *ctx ) {} NTSTATUS bluez_dbus_loop( void *c, void *watcher, struct winebluetooth_event *result ) { return STATUS_NOT_SUPPORTED; diff --git a/dlls/winebth.sys/unixlib.c b/dlls/winebth.sys/unixlib.c index 982652498e5..b7bf4012ce3 100644 --- a/dlls/winebth.sys/unixlib.c +++ b/dlls/winebth.sys/unixlib.c @@ -115,6 +115,7 @@ static NTSTATUS bluetooth_shutdown( void *params ) if (!dbus_connection) return STATUS_NOT_SUPPORTED;
bluez_dbus_close( dbus_connection ); + bluez_watcher_close( dbus_connection, bluetooth_watcher ); bluez_dbus_free( dbus_connection ); return STATUS_SUCCESS; }
From: Vibhav Pant vibhavp@gmail.com
--- MAINTAINERS | 2 +- dlls/bluetoothapis/bluetoothapis.spec | 2 +- dlls/bluetoothapis/main.c | 10 ++++++++++ dlls/bthprops.cpl/bthprops.cpl.spec | 2 +- 4 files changed, 13 insertions(+), 3 deletions(-)
diff --git a/MAINTAINERS b/MAINTAINERS index 2510938d94e..87e2895ddf3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -52,7 +52,7 @@ F: dlls/xaudio*/ Bluetooth support M: Vibhav Pant vibhavp@gmail.com F: dlls/winebth.sys/ -F: dlls/bluetoothapis/sdp.c +F: dlls/bluetoothapis/
Common Controls Library P: Nikolay Sivov nsivov@codeweavers.com diff --git a/dlls/bluetoothapis/bluetoothapis.spec b/dlls/bluetoothapis/bluetoothapis.spec index a28a493311d..5277ff825be 100644 --- a/dlls/bluetoothapis/bluetoothapis.spec +++ b/dlls/bluetoothapis/bluetoothapis.spec @@ -1,7 +1,7 @@ @ stub BluetoothAddressToString @ stub BluetoothDisconnectDevice @ stub BluetoothEnableDiscovery -@ stub BluetoothEnableIncomingConnections +@ stdcall BluetoothEnableIncomingConnections(ptr long) @ stub BluetoothEnumerateInstalledServices @ stub BluetoothEnumerateInstalledServicesEx @ stub BluetoothEnumerateLocalServices diff --git a/dlls/bluetoothapis/main.c b/dlls/bluetoothapis/main.c index fd3496e3fab..299eddbc876 100644 --- a/dlls/bluetoothapis/main.c +++ b/dlls/bluetoothapis/main.c @@ -2,6 +2,7 @@ * Bluetooth APIs * * Copyright 2016 Austin English + * Copyright 2025 Vibhav Pant * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -263,6 +264,15 @@ BOOL WINAPI BluetoothIsConnectable( HANDLE radio ) } }
+/********************************************************************* + * BluetoothEnableIncomingConnections + */ +BOOL WINAPI BluetoothEnableIncomingConnections( HANDLE radio, BOOL enable ) +{ + FIXME( "(%p, %d): stub!\n", radio, enable ); + return FALSE; +} + /********************************************************************* * BluetoothIsDiscoverable */ diff --git a/dlls/bthprops.cpl/bthprops.cpl.spec b/dlls/bthprops.cpl/bthprops.cpl.spec index 751eec41e79..2e72c48dcc4 100644 --- a/dlls/bthprops.cpl/bthprops.cpl.spec +++ b/dlls/bthprops.cpl/bthprops.cpl.spec @@ -6,7 +6,7 @@ @ stub BluetoothDisconnectDevice @ stub BluetoothDisplayDeviceProperties @ stub BluetoothEnableDiscovery -@ stub BluetoothEnableIncomingConnections +@ stdcall -import BluetoothEnableIncomingConnections(ptr long) @ stub BluetoothEnumerateInstalledServices @ stub BluetoothEnumerateInstalledServicesEx @ stub BluetoothFindBrowseGroupClose
From: Vibhav Pant vibhavp@gmail.com
--- dlls/bluetoothapis/tests/radio.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+)
diff --git a/dlls/bluetoothapis/tests/radio.c b/dlls/bluetoothapis/tests/radio.c index 60e0a7f4ebc..d16dfcde102 100644 --- a/dlls/bluetoothapis/tests/radio.c +++ b/dlls/bluetoothapis/tests/radio.c @@ -219,6 +219,35 @@ void test_BluetoothIsDiscoverable( void ) ok( ret == result, "%d != %d\n", ret, result ); }
+void test_radio_BluetoothEnableIncomingConnections( HANDLE radio, void *data ) +{ + BOOL connectable; + BOOL *result = data; + + *result |= BluetoothEnableIncomingConnections( radio, TRUE ); + todo_wine ok( *result, "%d != 1\n", *result ); + if (*result) + { + connectable = BluetoothIsConnectable( radio ); + ok( connectable, "%d != 1\n", connectable ); + } + else + skip("BluetoothEnableIncomingConnections failed, skipping.\n"); +} + +void test_BluetoothEnableIncomingConnections( void ) +{ + BOOL result = FALSE; + + test_for_all_radios( test_radio_BluetoothEnableIncomingConnections, &result ); + if (result) + { + BOOL connectable; + connectable = BluetoothIsConnectable( NULL ); + ok( connectable, "%d != 1\n", connectable ); + } +} + START_TEST( radio ) { test_BluetoothFindFirstRadio(); @@ -228,4 +257,5 @@ START_TEST( radio ) test_for_all_radios( test_BluetoothGetRadioInfo, NULL ); test_BluetoothIsDiscoverable(); test_BluetoothIsConnectable(); + test_BluetoothEnableIncomingConnections(); }
From: Vibhav Pant vibhavp@gmail.com
--- dlls/bluetoothapis/main.c | 47 ++++++++++++++++++++++++++++---- dlls/bluetoothapis/tests/radio.c | 2 +- 2 files changed, 43 insertions(+), 6 deletions(-)
diff --git a/dlls/bluetoothapis/main.c b/dlls/bluetoothapis/main.c index 299eddbc876..646f73f44e6 100644 --- a/dlls/bluetoothapis/main.c +++ b/dlls/bluetoothapis/main.c @@ -32,6 +32,7 @@ #include "bluetoothapis.h" #include "setupapi.h" #include "winioctl.h" +#include "wine/winebth.h"
#include "initguid.h" #include "bthdef.h" @@ -213,9 +214,6 @@ DWORD WINAPI BluetoothGetRadioInfo( HANDLE radio, PBLUETOOTH_RADIO_INFO info ) return ERROR_SUCCESS; }
-#define LOCAL_RADIO_DISCOVERABLE 0x0001 -#define LOCAL_RADIO_CONNECTABLE 0x0002 - /********************************************************************* * BluetoothIsConnectable */ @@ -269,8 +267,47 @@ BOOL WINAPI BluetoothIsConnectable( HANDLE radio ) */ BOOL WINAPI BluetoothEnableIncomingConnections( HANDLE radio, BOOL enable ) { - FIXME( "(%p, %d): stub!\n", radio, enable ); - return FALSE; + TRACE( "(%p, %d)\n", radio, enable ); + if (!radio) + { + BLUETOOTH_FIND_RADIO_PARAMS params = {.dwSize = sizeof( params )}; + HBLUETOOTH_RADIO_FIND find = BluetoothFindFirstRadio( ¶ms, &radio ); + + if (!find) + return FALSE; + for (;;) + { + if (BluetoothEnableIncomingConnections( radio, enable )) + { + CloseHandle( radio ); + BluetoothFindRadioClose( find ); + return TRUE; + } + + CloseHandle(radio ); + if (!BluetoothFindNextRadio( find, &radio )) + { + BluetoothFindRadioClose( find ); + return FALSE; + } + } + } + else if (!enable && !BluetoothIsDiscoverable( radio )) + /* The local radio can only be made non-connectable if it is non-discoverable. */ + return FALSE; + else + { + struct winebth_radio_set_flag_params params = {0}; + BOOL ret; + DWORD bytes; + + params.flag = LOCAL_RADIO_CONNECTABLE; + params.enable = !!enable; + ret = DeviceIoControl( radio, IOCTL_WINEBTH_RADIO_SET_FLAG, ¶ms, sizeof( params ), NULL, 0, &bytes, NULL ); + if (!ret) + ERR("DeviceIoControl failed: %#lx\n", GetLastError()); + return ret; + } }
/********************************************************************* diff --git a/dlls/bluetoothapis/tests/radio.c b/dlls/bluetoothapis/tests/radio.c index d16dfcde102..1f967e43e06 100644 --- a/dlls/bluetoothapis/tests/radio.c +++ b/dlls/bluetoothapis/tests/radio.c @@ -225,7 +225,7 @@ void test_radio_BluetoothEnableIncomingConnections( HANDLE radio, void *data ) BOOL *result = data;
*result |= BluetoothEnableIncomingConnections( radio, TRUE ); - todo_wine ok( *result, "%d != 1\n", *result ); + ok( *result, "%d != 1\n", *result ); if (*result) { connectable = BluetoothIsConnectable( radio );
From: Vibhav Pant vibhavp@gmail.com
--- dlls/bluetoothapis/bluetoothapis.spec | 2 +- dlls/bluetoothapis/main.c | 9 +++++++++ dlls/bthprops.cpl/bthprops.cpl.spec | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/dlls/bluetoothapis/bluetoothapis.spec b/dlls/bluetoothapis/bluetoothapis.spec index 5277ff825be..77a3a931a55 100644 --- a/dlls/bluetoothapis/bluetoothapis.spec +++ b/dlls/bluetoothapis/bluetoothapis.spec @@ -1,6 +1,6 @@ @ stub BluetoothAddressToString @ stub BluetoothDisconnectDevice -@ stub BluetoothEnableDiscovery +@ stdcall BluetoothEnableDiscovery(ptr long) @ stdcall BluetoothEnableIncomingConnections(ptr long) @ stub BluetoothEnumerateInstalledServices @ stub BluetoothEnumerateInstalledServicesEx diff --git a/dlls/bluetoothapis/main.c b/dlls/bluetoothapis/main.c index 646f73f44e6..8a3acc781bd 100644 --- a/dlls/bluetoothapis/main.c +++ b/dlls/bluetoothapis/main.c @@ -358,6 +358,15 @@ BOOL WINAPI BluetoothIsDiscoverable( HANDLE radio ) } }
+/********************************************************************* + * BluetoothEnableDiscovery + */ +BOOL WINAPI BluetoothEnableDiscovery( HANDLE radio, BOOL enabled ) +{ + FIXME("(%p %d): stub!\n", radio, enabled); + return FALSE; +} + /********************************************************************* * BluetoothFindNextDevice */ diff --git a/dlls/bthprops.cpl/bthprops.cpl.spec b/dlls/bthprops.cpl/bthprops.cpl.spec index 2e72c48dcc4..dee5544baaf 100644 --- a/dlls/bthprops.cpl/bthprops.cpl.spec +++ b/dlls/bthprops.cpl/bthprops.cpl.spec @@ -5,7 +5,7 @@ @ stub BluetoothAuthenticationAgent @ stub BluetoothDisconnectDevice @ stub BluetoothDisplayDeviceProperties -@ stub BluetoothEnableDiscovery +@ stdcall -import BluetoothEnableDiscovery(ptr long) @ stdcall -import BluetoothEnableIncomingConnections(ptr long) @ stub BluetoothEnumerateInstalledServices @ stub BluetoothEnumerateInstalledServicesEx
From: Vibhav Pant vibhavp@gmail.com
--- dlls/bluetoothapis/tests/radio.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+)
diff --git a/dlls/bluetoothapis/tests/radio.c b/dlls/bluetoothapis/tests/radio.c index 1f967e43e06..903f6010edc 100644 --- a/dlls/bluetoothapis/tests/radio.c +++ b/dlls/bluetoothapis/tests/radio.c @@ -248,6 +248,33 @@ void test_BluetoothEnableIncomingConnections( void ) } }
+void test_radio_BluetoothEnableDiscovery( HANDLE radio, void *data ) +{ + BOOL ret; + + if (BluetoothIsDiscoverable( radio )) + { + ret = BluetoothEnableDiscovery( radio, FALSE ); + todo_wine ok( ret, "%d != 1\n", ret ); + } + + /* Enabling discovery requires incoming connections to be enabled as well, so this should result in an error. */ + BluetoothEnableIncomingConnections( radio, FALSE ); + ret = BluetoothEnableDiscovery( radio, TRUE ); + ok( !ret, "%d != 0\n", ret ); + + BluetoothEnableIncomingConnections( radio, TRUE ); + ret = BluetoothEnableDiscovery( radio, TRUE ); + todo_wine ok( ret, "%d != 1\n", ret ); + ret = BluetoothIsDiscoverable( radio ); + todo_wine ok ( ret, "%d != 1\n", ret ); +} + +void test_BluetoothEnableDiscovery( void ) +{ + test_for_all_radios( test_radio_BluetoothEnableDiscovery, NULL ); +} + START_TEST( radio ) { test_BluetoothFindFirstRadio(); @@ -258,4 +285,5 @@ START_TEST( radio ) test_BluetoothIsDiscoverable(); test_BluetoothIsConnectable(); test_BluetoothEnableIncomingConnections(); + test_BluetoothEnableDiscovery(); }
From: Vibhav Pant vibhavp@gmail.com
--- dlls/bluetoothapis/main.c | 43 ++++++++++++++++++++++++++++++-- dlls/bluetoothapis/tests/radio.c | 6 ++--- 2 files changed, 44 insertions(+), 5 deletions(-)
diff --git a/dlls/bluetoothapis/main.c b/dlls/bluetoothapis/main.c index 8a3acc781bd..6cdc84f6b6f 100644 --- a/dlls/bluetoothapis/main.c +++ b/dlls/bluetoothapis/main.c @@ -363,8 +363,47 @@ BOOL WINAPI BluetoothIsDiscoverable( HANDLE radio ) */ BOOL WINAPI BluetoothEnableDiscovery( HANDLE radio, BOOL enabled ) { - FIXME("(%p %d): stub!\n", radio, enabled); - return FALSE; + TRACE( "(%p, %d)\n", radio, enabled ); + if (!radio) + { + BLUETOOTH_FIND_RADIO_PARAMS params = {.dwSize = sizeof( params )}; + HBLUETOOTH_RADIO_FIND find = BluetoothFindFirstRadio( ¶ms, &radio ); + + if (!find) + return FALSE; + for (;;) + { + if (BluetoothEnableDiscovery( radio, enabled )) + { + CloseHandle( radio ); + BluetoothFindRadioClose( find ); + return TRUE; + } + + CloseHandle(radio ); + if (!BluetoothFindNextRadio( find, &radio )) + { + BluetoothFindRadioClose( find ); + return FALSE; + } + } + } + else if (enabled && !BluetoothIsConnectable( radio )) + /* The local radio can only be made discoverable if it is connectable. */ + return FALSE; + else + { + struct winebth_radio_set_flag_params params = {0}; + BOOL ret; + DWORD bytes; + + params.flag = LOCAL_RADIO_DISCOVERABLE; + params.enable = !!enabled; + ret = DeviceIoControl( radio, IOCTL_WINEBTH_RADIO_SET_FLAG, ¶ms, sizeof( params ), NULL, 0, &bytes, NULL ); + if (!ret) + ERR("DeviceIoControl failed: %#lx\n", GetLastError()); + return ret; + } }
/********************************************************************* diff --git a/dlls/bluetoothapis/tests/radio.c b/dlls/bluetoothapis/tests/radio.c index 903f6010edc..798c5ddce20 100644 --- a/dlls/bluetoothapis/tests/radio.c +++ b/dlls/bluetoothapis/tests/radio.c @@ -255,7 +255,7 @@ void test_radio_BluetoothEnableDiscovery( HANDLE radio, void *data ) if (BluetoothIsDiscoverable( radio )) { ret = BluetoothEnableDiscovery( radio, FALSE ); - todo_wine ok( ret, "%d != 1\n", ret ); + ok( ret, "%d != 1\n", ret ); }
/* Enabling discovery requires incoming connections to be enabled as well, so this should result in an error. */ @@ -265,9 +265,9 @@ void test_radio_BluetoothEnableDiscovery( HANDLE radio, void *data )
BluetoothEnableIncomingConnections( radio, TRUE ); ret = BluetoothEnableDiscovery( radio, TRUE ); - todo_wine ok( ret, "%d != 1\n", ret ); + ok( ret, "%d != 1\n", ret ); ret = BluetoothIsDiscoverable( radio ); - todo_wine ok ( ret, "%d != 1\n", ret ); + ok ( ret, "%d != 1\n", ret ); }
void test_BluetoothEnableDiscovery( void )
On Tue Feb 18 20:39:57 2025 +0000, Vibhav Pant wrote:
It's bikeshedding, and I won't push very hard on this issue, but I
can't say I see any reason to keep the unix side only using one set of constants. If anything it seems more obscure to me; it's clearer to do as little conversion as possible. Hm, I don't see this function being used for other flags in the future, so using `LOCAL_RADIO_*` is definitely simpler. I've changed the code to using them.
I'd recommend it. I don't know that the bitfields _won't_ work, they
very well may, but they're not really necessary either. Sure, I'll try testing to see if the bitfields work for the next MR.
Hmm, well, if we can't rely on bitfields, we may have bigger problems anyway; several ntdll structs use them, e.g. MEM_EXTENDED_PARAMETER, XSTATE_CONFIGURATION, LDT_ENTRY.
This merge request was approved by Elizabeth Figura.
Thanks, cursory review looks good now.
For the record—while "tests before fix" is generally a good practice, I would argue that it's not always necessary, and especially not necessary when it's not a fix but a new feature. In such cases I'd advocate for putting the implementation first, or if it's simple enough (like here) squash them together even.