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.
-- v15: 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..58910a80819 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, %#x, %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 )