This MR should be the first of at least three 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 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.
From: Vibhav Pant vibhavp@gmail.com
--- configure.ac | 1 + dlls/winebth.sys/Makefile.in | 8 ++ dlls/winebth.sys/winebth.c | 158 ++++++++++++++++++++++++++++++++ dlls/winebth.sys/winebth.inf | 22 +++++ dlls/winebth.sys/winebth.rc | 20 ++++ dlls/winebth.sys/winebth_priv.h | 88 ++++++++++++++++++ loader/wine.inf.in | 1 + programs/wineboot/wineboot.c | 1 + 8 files changed, 299 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 ea406572a58..781a6ba09f6 100644 --- a/configure.ac +++ b/configure.ac @@ -3236,6 +3236,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..44a966f3ac1 --- /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..699c9915e27 --- /dev/null +++ b/dlls/winebth.sys/winebth.c @@ -0,0 +1,158 @@ +/* + * 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_QUERY_DEVICE_RELATIONS: + break; + 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_DEVICE_RELATIONS: + break; + case IRP_MN_QUERY_ID: + break; + case IRP_MN_QUERY_CAPABILITIES: + { + DEVICE_CAPABILITIES *caps = stack->Parameters.DeviceCapabilities.Capabilities; + caps->Removable = TRUE; + caps->SurpriseRemovalOK = TRUE; + caps->RawDeviceOK = TRUE; + ret = STATUS_SUCCESS; + break; + } + case IRP_MN_START_DEVICE: + ret = STATUS_SUCCESS; + break; + case IRP_MN_REMOVE_DEVICE: + { + irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest( irp, IO_NO_INCREMENT ); + return STATUS_SUCCESS; + } + case IRP_MN_SURPRISE_REMOVAL: + ret = STATUS_SUCCESS; + break; + } + + irp->IoStatus.Status = ret; + IoCompleteRequest( irp, IO_NO_INCREMENT ); + return ret; +} + +static NTSTATUS WINAPI bluetooth_pnp( DEVICE_OBJECT *device, IRP *irp ) +{ + if (device == bus_fdo) + return fdo_pnp( device, irp ); + return pdo_pnp( device, irp ); +} + +static NTSTATUS WINAPI driver_add_device( DRIVER_OBJECT *driver, DEVICE_OBJECT *pdo ) +{ + NTSTATUS ret; + + TRACE( "(%p, %p)\n", driver, pdo ); + ret = IoCreateDevice( driver, 0, NULL, FILE_DEVICE_BUS_EXTENDER, 0, FALSE, &bus_fdo ); + if (ret != STATUS_SUCCESS) + { + ERR( "failed to create FDO: %#lx\n", ret ); + return ret; + } + + IoAttachDeviceToDeviceStack( bus_fdo, pdo ); + bus_pdo = pdo; + bus_fdo->Flags &= ~DO_DEVICE_INITIALIZING; + return STATUS_SUCCESS; +} + +static void WINAPI driver_unload( DRIVER_OBJECT *driver ) +{ + FIXME( "(%p) stub!\n", driver ); +} + +NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) +{ + TRACE( "(%p, %s)\n", driver, debugstr_w( path->Buffer ) ); + + driver->DriverExtension->AddDevice = driver_add_device; + driver_obj = driver; + + driver->MajorFunction[IRP_MJ_PNP] = bluetooth_pnp; + driver->DriverUnload = driver_unload; + + return STATUS_SUCCESS; +} diff --git a/dlls/winebth.sys/winebth.inf b/dlls/winebth.sys/winebth.inf new file mode 100644 index 00000000000..6b3b8be0ed0 --- /dev/null +++ b/dlls/winebth.sys/winebth.inf @@ -0,0 +1,22 @@ +[Version] +Signature="$CHICAGO$" +ClassGuid={4d36e97d-e325-11ce-bfc1-08002be10318} +Class=System + +[Manufacturer] +Wine=mfg_section + +[mfg_section] +Wine Bluetooth bus driver=device_section,root\winebth + +[device_section.Services] +AddService = winebth,0x2,svc_section + +[svc_section] +Description="Wine Bluetooth bus driver" +DisplayName="Wine Bluetooth bus" +ServiceBinary="%12%\winebth.sys" +LoadOrderGroup="WinePlugPlay" +ServiceType=1 +StartType=3 +ErrorControl=1 diff --git a/dlls/winebth.sys/winebth.rc b/dlls/winebth.sys/winebth.rc new file mode 100644 index 00000000000..33157042b7c --- /dev/null +++ b/dlls/winebth.sys/winebth.rc @@ -0,0 +1,20 @@ +/* + * Copyright 2024 Vibhav Pant + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* @makedep: winebth.inf */ +1 WINE_DATA_FILE winebth.inf diff --git a/dlls/winebth.sys/winebth_priv.h b/dlls/winebth.sys/winebth_priv.h new file mode 100644 index 00000000000..ef29ed2f8c0 --- /dev/null +++ b/dlls/winebth.sys/winebth_priv.h @@ -0,0 +1,88 @@ +/* + * 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_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 ) +{ + 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
The unixlib implementation currently supports Linux using BlueZ over DBus. --- dlls/winebth.sys/Makefile.in | 8 +- dlls/winebth.sys/dbus.c | 789 +++++++++++++++++++++++++++++++ dlls/winebth.sys/dbus.h | 113 +++++ dlls/winebth.sys/unixlib.c | 188 ++++++++ dlls/winebth.sys/unixlib.h | 116 +++++ dlls/winebth.sys/unixlib_priv.h | 55 +++ dlls/winebth.sys/winebluetooth.c | 114 +++++ dlls/winebth.sys/winebluetooth.h | 147 ++++++ dlls/winebth.sys/winebth.c | 413 +++++++++++++++- dlls/winebth.sys/winebth_priv.h | 5 + include/Makefile.in | 2 + include/bthdef.h | 33 ++ include/ddk/bthguid.h | 57 +++ 13 files changed, 2035 insertions(+), 5 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 create mode 100644 include/bthdef.h create mode 100644 include/ddk/bthguid.h
diff --git a/dlls/winebth.sys/Makefile.in b/dlls/winebth.sys/Makefile.in index 44a966f3ac1..f34f71d5ca5 100644 --- a/dlls/winebth.sys/Makefile.in +++ b/dlls/winebth.sys/Makefile.in @@ -1,8 +1,14 @@ MODULE = winebth.sys -IMPORTS = ntoskrnl +IMPORTS = ntoskrnl uuid +UNIXLIB = winebth.so +UNIX_CFLAGS = $(DBUS_CFLAGS)
EXTRADLLFLAGS = -Wl,--subsystem,native
SOURCES = \ winebth.c \ + bthenum.c \ + dbus.c \ + unixlib.c \ + winebluetooth.c \ winebth.rc diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c new file mode 100644 index 00000000000..4878d9dc8a4 --- /dev/null +++ b/dlls/winebth.sys/dbus.c @@ -0,0 +1,789 @@ +/* + * Support for communicating with BlueZ over DBus. + * + * Copyright 2023 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> +#include <assert.h> +#include <pthread.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 <bthsdpdef.h> +#include <bluetoothapis.h> + +#include <wine/debug.h> + +#include "winebluetooth.h" + +#include "unixlib.h" +#include "unixlib_priv.h" +#include "dbus.h" + +WINE_DEFAULT_DEBUG_CHANNEL( winebluetooth ); +WINE_DECLARE_DEBUG_CHANNEL( dbus ); + +#ifdef SONAME_LIBDBUS_1 + +const int bluez_timeout = -1; + +#define DBUS_INTERFACE_OBJECTMANAGER "org.freedesktop.DBus.ObjectManager" +#define DBUS_OBJECTMANAGER_SIGNAL_INTERFACESADDED "InterfacesAdded" +#define DBUS_OBJECTMANAGER_SIGNAL_INTERFACESREMOVED "InterfacesRemoved" + +#define DBUS_INTERFACES_ADDED_SIGNATURE \ + DBUS_TYPE_OBJECT_PATH_AS_STRING \ + DBUS_TYPE_ARRAY_AS_STRING \ + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING \ + DBUS_TYPE_STRING_AS_STRING \ + DBUS_TYPE_ARRAY_AS_STRING \ + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING \ + DBUS_DICT_ENTRY_END_CHAR_AS_STRING \ + DBUS_DICT_ENTRY_END_CHAR_AS_STRING + +#define DBUS_INTERFACES_REMOVED_SIGNATURE \ + DBUS_TYPE_OBJECT_PATH_AS_STRING \ + DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING + +#define DBUS_PROPERTIES_CHANGED_SIGNATURE \ + DBUS_TYPE_STRING_AS_STRING \ + DBUS_TYPE_ARRAY_AS_STRING \ + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING \ + DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING \ + DBUS_DICT_ENTRY_END_CHAR_AS_STRING \ + DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING + +#define BLUEZ_DEST "org.bluez" +#define BLUEZ_INTERFACE_ADAPTER "org.bluez.Adapter1" + +#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 NTSTATUS bluez_dbus_error_to_ntstatus( const DBusError *error ) +{ + +#define DBUS_ERROR_CASE(n, s) if(p_dbus_error_has_name( error, (n)) ) return (s) + + DBUS_ERROR_CASE( "org.bluez.Error.Failed", STATUS_INTERNAL_ERROR); + DBUS_ERROR_CASE( "org.bluez.Error.NotReady", STATUS_DEVICE_NOT_READY ); + DBUS_ERROR_CASE( "org.bluez.Error.NotAuthorized", STATUS_ACCESS_DENIED ); + DBUS_ERROR_CASE( "org.bluez.Error.InvalidArguments", STATUS_INVALID_PARAMETER ); + DBUS_ERROR_CASE( "org.bluez.Error.AlreadyExists", STATUS_NO_MORE_ENTRIES ); + DBUS_ERROR_CASE( "org.bluez.Error.AuthenticationCanceled", STATUS_CANCELLED ); + DBUS_ERROR_CASE( "org.bluez.Error.AuthenticationFailed", STATUS_INTERNAL_ERROR ); + DBUS_ERROR_CASE( "org.bluez.Error.AuthenticationRejected", STATUS_INTERNAL_ERROR ); + DBUS_ERROR_CASE( "org.bluez.Error.AuthenticationTimeout", STATUS_TIMEOUT ); + DBUS_ERROR_CASE( "org.bluez.Error.ConnectionAttemptFailed", STATUS_DEVICE_NOT_CONNECTED); + DBUS_ERROR_CASE( "org.bluez.Error.NotConnected", STATUS_DEVICE_NOT_CONNECTED ); + DBUS_ERROR_CASE( "org.bluez.Error.InProgress", STATUS_OPERATION_IN_PROGRESS ); + DBUS_ERROR_CASE( DBUS_ERROR_UNKNOWN_OBJECT, STATUS_INVALID_PARAMETER ); + DBUS_ERROR_CASE( DBUS_ERROR_NO_MEMORY, STATUS_NO_MEMORY ); + DBUS_ERROR_CASE( DBUS_ERROR_NOT_SUPPORTED, STATUS_NOT_SUPPORTED ); + DBUS_ERROR_CASE( DBUS_ERROR_ACCESS_DENIED, STATUS_ACCESS_DENIED ); + return STATUS_INTERNAL_ERROR; +#undef DBUS_ERROR_CASE +} + +static const char *bluez_next_dict_entry( DBusMessageIter *iter, DBusMessageIter *variant ) +{ + DBusMessageIter sub; + 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 const char *dbgstr_dbus_message( DBusMessage *message ) +{ + const char *interface; + const char *member; + const char *path; + const char *sender; + const char *signature; + int type; + + interface = p_dbus_message_get_interface( message ); + member = p_dbus_message_get_member( message ); + path = p_dbus_message_get_path( message ); + sender = p_dbus_message_get_sender( message ); + type = p_dbus_message_get_type( message ); + signature = p_dbus_message_get_signature( message ); + + switch (type) + { + case DBUS_MESSAGE_TYPE_METHOD_CALL: + return wine_dbg_sprintf( + "{method_call sender=%s interface=%s member=%s path=%s signature='%s'}", + debugstr_a( sender ), debugstr_a( interface ), debugstr_a( member ), debugstr_a( path ), + debugstr_a( signature ) ); + case DBUS_MESSAGE_TYPE_SIGNAL: + return wine_dbg_sprintf( "{signal sender=%s interface=%s member=%s path=%s signature='%s'}", + debugstr_a( sender ), debugstr_a( interface ), + debugstr_a( member ), debugstr_a( path ), + debugstr_a( signature ) ); + default: + return wine_dbg_sprintf( "%p", message ); + } +} + +static inline const char *dbgstr_dbus_connection( DBusConnection *connection ) +{ + return wine_dbg_sprintf( "{%p connected=%d}", connection, + 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 + + +static void parse_mac_address( const char *addr_str, BYTE dest[6] ) +{ + int addr[6], i; + + sscanf( addr_str, "%x:%x:%x:%x:%x:%x", &addr[0], &addr[1], &addr[2], &addr[3], &addr[4], + &addr[5] ); + for (i = 0 ; i < 6; i++) + dest[i] = addr[i]; +} + +static void bluez_radio_prop_from_dict_entry( const char *prop_name, DBusMessageIter *variant, + struct winebluetooth_radio_properties *props, + winebluetooth_radio_props_mask_t *props_mask, + winebluetooth_radio_props_mask_t wanted_props_mask ) +{ + TRACE_( dbus )( "(%s, %p, %p, %p, %#x)\n", debugstr_a( prop_name ), variant, props, props_mask, wanted_props_mask ); + + if (wanted_props_mask & WINEBLUETOOTH_RADIO_PROPERTY_ADDRESS && + !strcmp( prop_name, "Address" ) && + p_dbus_message_iter_get_arg_type( variant ) == DBUS_TYPE_STRING) + { + const char *addr_str; + p_dbus_message_iter_get_basic( variant, &addr_str ); + parse_mac_address( addr_str, props->address.rgBytes ); + *props_mask |= WINEBLUETOOTH_RADIO_PROPERTY_ADDRESS; + } + else if (wanted_props_mask & WINEBLUETOOTH_RADIO_PROPERTY_CLASS && + !strcmp( prop_name, "Class" ) && + p_dbus_message_iter_get_arg_type( variant ) == DBUS_TYPE_UINT32) + { + dbus_uint32_t class; + p_dbus_message_iter_get_basic( variant, &class ); + props->class = class; + *props_mask |= WINEBLUETOOTH_RADIO_PROPERTY_CLASS; + } + else if (wanted_props_mask & WINEBLUETOOTH_RADIO_PROPERTY_MANUFACTURER && + !strcmp( prop_name, "Manufacturer" ) && + p_dbus_message_iter_get_arg_type( variant ) == DBUS_TYPE_UINT16) + { + dbus_uint16_t manufacturer; + p_dbus_message_iter_get_basic( variant, &manufacturer ); + props->manufacturer = manufacturer; + *props_mask |= WINEBLUETOOTH_RADIO_PROPERTY_MANUFACTURER; + } + else if (wanted_props_mask & WINEBLUETOOTH_RADIO_PROPERTY_CONNECTABLE && + !strcmp( prop_name, "Connectable" ) && + p_dbus_message_iter_get_arg_type( variant ) == DBUS_TYPE_BOOLEAN) + { + dbus_bool_t connectable; + p_dbus_message_iter_get_basic( variant, &connectable ); + props->connectable = connectable != 0; + *props_mask |= WINEBLUETOOTH_RADIO_PROPERTY_CONNECTABLE; + } + else if (wanted_props_mask & WINEBLUETOOTH_RADIO_PROPERTY_DISCOVERABLE && + !strcmp( prop_name, "Discoverable" ) && + p_dbus_message_iter_get_arg_type( variant ) == DBUS_TYPE_BOOLEAN) + { + dbus_bool_t discoverable; + p_dbus_message_iter_get_basic( variant, &discoverable ); + props->discoverable = discoverable != 0; + *props_mask |= WINEBLUETOOTH_RADIO_PROPERTY_DISCOVERABLE; + } + else if (wanted_props_mask & WINEBLUETOOTH_RADIO_PROPERTY_DISCOVERING && + !strcmp( prop_name, "Discovering") && + p_dbus_message_iter_get_arg_type( variant ) == DBUS_TYPE_BOOLEAN) + { + dbus_bool_t discovering; + p_dbus_message_iter_get_basic( variant, &discovering ); + props->discovering = discovering != 0; + *props_mask |= WINEBLUETOOTH_RADIO_PROPERTY_DISCOVERABLE; + } + else if (wanted_props_mask & WINEBLUETOOTH_RADIO_PROPERTY_PAIRABLE && + !strcmp( prop_name, "Pairable" ) && + p_dbus_message_iter_get_arg_type( variant ) == DBUS_TYPE_BOOLEAN) + { + dbus_bool_t pairable; + p_dbus_message_iter_get_basic( variant, &pairable ); + props->pairable = pairable != 0; + *props_mask |= WINEBLUETOOTH_RADIO_PROPERTY_PAIRABLE; + } +} + +struct bluez_pair_complete_params +{ + void *user_data; + struct list *event_list; +}; + +struct bluez_watcher_ctx +{ + void *init_device_list_call; + + /* struct bluez_init_entry */ + struct list initial_radio_list; + /* struct bluez_init_entry */ + struct list initial_device_list; + + /* struct bluez_watcher_event */ + struct list event_list; +}; + +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 ); +} + +struct bluez_watcher_event_pending_call +{ + struct list entry; + DBusPendingCall *call; +}; + +struct bluez_watcher_event +{ + struct list entry; + enum winebluetooth_watcher_event_type event_type; + union winebluetooth_watcher_event_data event; + + /* Some DBus signals, like PropertiesChanged in org.freedesktop.DBus.Properties, require us to + * perform an additional call to get the complete state of the object (in this instance, call + * Get/GetAll to get the values of invalidated properties). The event is queued out only once + * this call completes. */ + DBusPendingCall *pending_call; +}; + +static BOOL bluez_event_list_queue_new_event_with_call( + struct list *event_list, enum winebluetooth_watcher_event_type event_type, + union winebluetooth_watcher_event_data event, DBusPendingCall *call, + DBusPendingCallNotifyFunction callback ) +{ + struct bluez_watcher_event *event_entry; + + if (call != NULL) assert( callback != NULL ); + else assert( callback == NULL ); + + event_entry = calloc(1, sizeof( *event_entry ) ); + if (!event_entry) + { + ERR( "Could not allocate memory for DBus event.\n" ); + return FALSE; + } + + event_entry->event_type = event_type; + event_entry->event = event; + event_entry->pending_call = call; + if (call != NULL) + p_dbus_pending_call_set_notify(call, callback, &event_entry->event, NULL); + list_add_tail( event_list, &event_entry->entry ); + + return TRUE; +} + +static BOOL bluez_event_list_queue_new_event( struct list *event_list, + enum winebluetooth_watcher_event_type event_type, + union winebluetooth_watcher_event_data event ) +{ + return bluez_event_list_queue_new_event_with_call( event_list, event_type, event, NULL, NULL ); +} + +static DBusHandlerResult bluez_filter( DBusConnection *conn, DBusMessage *msg, void *user_data ) +{ + struct list *event_list; + SIZE_T init_count, final_count; + + if (TRACE_ON( dbus )) + { + TRACE_( dbus ) + ( "(%s, %s, %p)\n", dbgstr_dbus_connection( conn ), dbgstr_dbus_message( msg ), user_data ); + } + + event_list = &((struct bluez_watcher_ctx *)user_data)->event_list; + init_count = list_count( event_list ); + + if (p_dbus_message_is_signal( msg, DBUS_INTERFACE_OBJECTMANAGER, DBUS_OBJECTMANAGER_SIGNAL_INTERFACESADDED ) + && p_dbus_message_has_signature( msg, DBUS_INTERFACES_ADDED_SIGNATURE )) + { + DBusMessageIter iter, ifaces_iter; + const char *object_path; + + p_dbus_message_iter_init (msg, &iter); + p_dbus_message_iter_get_basic( &iter, &object_path ); + p_dbus_message_iter_next( &iter ); + p_dbus_message_iter_recurse(&iter, &ifaces_iter); + while (p_dbus_message_iter_has_next( &ifaces_iter )) + { + DBusMessageIter iface_entry; + const char *iface_name; + + p_dbus_message_iter_recurse( &ifaces_iter, &iface_entry ); + p_dbus_message_iter_get_basic( &iface_entry, &iface_name ); + if (strcmp( iface_name, BLUEZ_INTERFACE_ADAPTER ) == 0) + { + struct winebluetooth_watcher_event_radio_added radio_added = {0}; + struct unix_name *radio; + DBusMessageIter props_iter, variant; + const char *prop_name; + + p_dbus_message_iter_next( &iface_entry ); + p_dbus_message_iter_recurse( &iface_entry, &props_iter ); + + while((prop_name = bluez_next_dict_entry( &props_iter, &variant ))) + { + bluez_radio_prop_from_dict_entry( prop_name, &variant, &radio_added.props, + &radio_added.props_mask, + WINEBLUETOOTH_RADIO_ALL_PROPERTIES ); + } + + radio_added.radio.handle = radio = unix_name_get_or_create( object_path ); + if (radio_added.radio.handle == NULL) + { + ERR("failed to allocate memory for adapter path %s\n", object_path); + break; + } + else + { + union winebluetooth_watcher_event_data event = { .radio_added = radio_added }; + TRACE( "New BlueZ %s object added at %s: %p\n", BLUEZ_INTERFACE_ADAPTER, + object_path, radio_added.radio.handle ); + if (!bluez_event_list_queue_new_event( + event_list, BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED, event )) + { + unix_name_free( radio ); + } + } + } + p_dbus_message_iter_next( &ifaces_iter ); + } + } + else if (p_dbus_message_is_signal( msg, DBUS_INTERFACE_OBJECTMANAGER, DBUS_OBJECTMANAGER_SIGNAL_INTERFACESREMOVED ) + && p_dbus_message_has_signature( msg, DBUS_INTERFACES_REMOVED_SIGNATURE )) + { + const char *object_path; + char **interfaces; + int n_interfaces, i; + DBusError error; + dbus_bool_t success; + + p_dbus_error_init( &error ); + success = p_dbus_message_get_args( msg, &error, DBUS_TYPE_OBJECT_PATH, &object_path, + DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &interfaces, + &n_interfaces, DBUS_TYPE_INVALID ); + if (!success) + { + ERR_( dbus ) + ( "error getting arguments from message: '%s: '%s'\n", error.name, error.message ); + p_dbus_error_free( &error ); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + p_dbus_error_free( &error ); + for (i = 0; i < n_interfaces; i++) + { + if (strcmp( interfaces[i], BLUEZ_INTERFACE_ADAPTER ) == 0) + { + winebluetooth_radio_t radio; + union winebluetooth_watcher_event_data event; + radio.handle = unix_name_get_or_create( object_path ); + + if (!radio.handle) + { + ERR( "failed to allocate memory for adapter path %s\n", object_path ); + continue; + } + event.radio_removed = radio; + if (!bluez_event_list_queue_new_event( + event_list, BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_REMOVED, event )) + { + unix_name_free(radio.handle); + } + } + } + p_dbus_free_string_array( interfaces ); + } + + final_count = list_count( event_list ); + assert( final_count >= init_count ); + if (final_count > init_count) + TRACE( "Received %ld new watcher-related event(s) from BlueZ\n", (final_count - init_count) ); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static const char BLUEZ_MATCH_OBJECTMANAGER[] = "type='signal'," + "interface='org.freedesktop.DBus.ObjectManager'," + "sender='"BLUEZ_DEST"'," + "path='/'"; + +static const char *BLUEZ_MATCH_RULES[] = { BLUEZ_MATCH_OBJECTMANAGER }; + +NTSTATUS bluez_watcher_init( void *connection, void **ctx ) +{ + DBusError err; + NTSTATUS status; + DBusPendingCall *call; + struct bluez_watcher_ctx *watcher_ctx = + calloc( 1, sizeof( struct bluez_watcher_ctx ) ); + SIZE_T i; + + if (watcher_ctx == NULL) return STATUS_NO_MEMORY; + status = bluez_get_objects_async( connection, &call ); + if (status != STATUS_SUCCESS) + { + free( watcher_ctx ); + ERR( "could not create async GetManagedObjects call: %#x\n", (int)status); + return status; + } + watcher_ctx->init_device_list_call = call; + list_init( &watcher_ctx->initial_radio_list ); + list_init( &watcher_ctx->initial_device_list ); + + list_init( &watcher_ctx->event_list ); + + if (!p_dbus_connection_add_filter( connection, bluez_filter, watcher_ctx, free )) + { + p_dbus_pending_call_cancel( call ); + p_dbus_pending_call_unref( call ); + free( watcher_ctx ); + ERR( "could not add DBus filter\n" ); + return STATUS_NO_MEMORY; + } + p_dbus_error_init( &err ); + for (i = 0; i < ARRAY_SIZE( BLUEZ_MATCH_RULES ); i++) + { + TRACE( "Adding DBus match rule "%s"\n", BLUEZ_MATCH_RULES[i] ); + + p_dbus_bus_add_match( connection, BLUEZ_MATCH_RULES[i], &err ); + if (p_dbus_error_is_set( &err )) + { + NTSTATUS status = bluez_dbus_error_to_ntstatus( &err ); + ERR( "could not add DBus match "%s": '%s': '%s'\n", BLUEZ_MATCH_RULES[i], err.name, + err.message ); + p_dbus_pending_call_cancel( call ); + p_dbus_pending_call_unref( call ); + p_dbus_error_free( &err ); + free( watcher_ctx ); + return status; + } + } + p_dbus_error_free( &err ); + *ctx = watcher_ctx; + TRACE( "ctx=%p\n", ctx ); + return STATUS_SUCCESS; +} + +void bluez_watcher_close( void *connection, void *ctx ) +{ + p_dbus_bus_remove_match( connection, BLUEZ_MATCH_OBJECTMANAGER, NULL ); + p_dbus_connection_remove_filter( connection, bluez_filter, ctx ); +} + +struct bluez_init_entry +{ + union { + struct winebluetooth_watcher_event_radio_added radio; + } object; + struct list entry; +}; + +static NTSTATUS bluez_build_initial_device_lists( DBusMessage *reply, struct list *adapter_list, + struct list *device_list ) +{ + DBusMessageIter dict, paths_iter, iface_iter, prop_iter; + const char *path; + NTSTATUS status = STATUS_SUCCESS; + + 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 )) + { + const char *prop_name; + DBusMessageIter variant; + struct bluez_init_entry *init_device = calloc( 1, sizeof( *init_device ) ); + struct unix_name *radio_name; + + if (!init_device) + { + status = STATUS_NO_MEMORY; + goto done; + } + init_device->object.radio.radio.handle = radio_name = + unix_name_get_or_create( path ); + if (!radio_name) + { + free( init_device ); + status = STATUS_NO_MEMORY; + goto done; + } + while ((prop_name = bluez_next_dict_entry( &prop_iter, &variant ))) + { + bluez_radio_prop_from_dict_entry( + prop_name, &variant, &init_device->object.radio.props, + &init_device->object.radio.props_mask, WINEBLUETOOTH_RADIO_ALL_PROPERTIES ); + } + list_add_tail( adapter_list, &init_device->entry ); + TRACE( "Found BlueZ %s object '%s': %p\n", BLUEZ_INTERFACE_ADAPTER, radio_name->str, + init_device->object.radio.radio.handle ); + break; + } + } + } + + TRACE( "Initial device list: radios: %d, devices: %d\n", + list_count( adapter_list ), list_count( device_list ) ); + done: + return status; +} + +static struct bluez_init_entry *bluez_init_entries_list_pop( struct list *list ) +{ + struct list *entry = list_head( list ); + struct bluez_init_entry *device = LIST_ENTRY( entry, struct bluez_init_entry, entry ); + + list_remove( entry ); + return device; +} + +static struct bluez_watcher_event *bluez_watcher_event_queue_ready( struct bluez_watcher_ctx *ctx ) +{ + struct list *head = list_head( &ctx->event_list ); + struct bluez_watcher_event *event = LIST_ENTRY(head, struct bluez_watcher_event, entry); + TRACE("(%p)\n", ctx); + + if (head == NULL) return NULL; + if (event->pending_call == NULL || p_dbus_pending_call_get_completed( event->pending_call )) + { + list_remove( &event->entry ); + if (event->pending_call) p_dbus_pending_call_unref( event->pending_call ); + return event; + } + return NULL; +} + +NTSTATUS bluez_dbus_loop( void *c, void *watcher, + struct winebluetooth_event_loop_result *result ) +{ + DBusConnection *connection; + struct bluez_watcher_ctx *watcher_ctx = watcher; + + TRACE( "(%p, %p, %p)\n", c, watcher, result ); + connection = p_dbus_connection_ref( c ); + + while(TRUE) + { + struct bluez_watcher_event *bluez_event; + + if (!list_empty( &watcher_ctx->initial_radio_list )) + { + struct bluez_init_entry *radio = + bluez_init_entries_list_pop( &watcher_ctx->initial_radio_list ); + struct winebluetooth_watcher_event *watcher_event = &result->data.watcher_event; + + result->status = WINEBLUETOOTH_EVENT_LOOP_STATUS_WATCHER_EVENT; + watcher_event->event_type = BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED; + watcher_event->event_data.radio_added = radio->object.radio; + free( radio ); + p_dbus_connection_unref( connection ); + return STATUS_PENDING; + } + else if ((bluez_event = bluez_watcher_event_queue_ready( watcher_ctx ))) + { + struct winebluetooth_watcher_event *watcher_event = &result->data.watcher_event; + + assert( bluez_event->pending_call == NULL ); + result->status = WINEBLUETOOTH_EVENT_LOOP_STATUS_WATCHER_EVENT; + watcher_event->event_type = bluez_event->event_type; + watcher_event->event_data = bluez_event->event; + free( bluez_event ); + p_dbus_connection_unref( connection ); + return STATUS_PENDING; + } + else if (!p_dbus_connection_read_write_dispatch( connection, -1 )) + { + p_dbus_connection_unref( connection ); + 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; + + TRACE_( dbus ) + ( "Received GetManagedObjects reply: %s\n", dbgstr_dbus_message( reply ) ); + p_dbus_pending_call_unref( watcher_ctx->init_device_list_call ); + watcher_ctx->init_device_list_call = NULL; + + p_dbus_error_init( &error ); + if (p_dbus_set_error_from_message( &error, reply )) + { + ERR( "error getting object list from BlueZ: '%s': '%s'\n", error.name, + error.message ); + p_dbus_error_free( &error ); + p_dbus_message_unref( reply ); + p_dbus_connection_unref( connection ); + return STATUS_NO_MEMORY; + } + status = bluez_build_initial_device_lists( reply, &watcher_ctx->initial_radio_list, + &watcher_ctx->initial_device_list ); + p_dbus_message_unref( reply ); + if (status != STATUS_SUCCESS) + { + ERR( "error building initial bluetooth devices list: %#x\n", (int)status ); + p_dbus_connection_unref( connection ); + return status; + } + } + } +} +#else + +#endif /* SONAME_LIBDBUS_1 */ diff --git a/dlls/winebth.sys/dbus.h b/dlls/winebth.sys/dbus.h new file mode 100644 index 00000000000..5859b6a4aef --- /dev/null +++ b/dlls/winebth.sys/dbus.h @@ -0,0 +1,113 @@ +/* + * DBus declarations. + * + * Copyright 2023 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..56c51f5d76f --- /dev/null +++ b/dlls/winebth.sys/unixlib.c @@ -0,0 +1,188 @@ +/* + * 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 <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( winebluetooth ); + +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 == NULL) + { + struct unix_name *s = malloc( sizeof( struct unix_name ) ); + if (s == NULL) + { + 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 ) +{ + 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; +} + +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] = isalpha( name->str[i] ) ? toupper( name->str[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; + 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 ) +{ + struct bluetooth_watcher_close_params *params = args; + + if ( !dbus_connection ) return STATUS_NOT_SUPPORTED; + bluez_watcher_close(dbus_connection, params->unix_watcher_ctx); + return STATUS_SUCCESS; +} + +static NTSTATUS bluetooth_event_loop_once( void *args ) +{ + struct bluetooth_event_loop_once_params *params = args; + + if (!dbus_connection) return STATUS_NOT_SUPPORTED; + memset( ¶ms->result, 0, sizeof( params->result ) ); + return bluez_dbus_loop( dbus_connection, params->unix_watcher_ctx, ¶ms->result ); +} + +const unixlib_entry_t __wine_unix_call_funcs[] = { + bluetooth_init, + bluetooth_shutdown, + + bluetooth_adapter_get_unique_name, + bluetooth_adapter_free, + + bluetooth_watcher_init, + bluetooth_watcher_close, + + bluetooth_event_loop_once, +}; + +C_ASSERT( ARRAYSIZE( __wine_unix_call_funcs ) == unix_funcs_count ); diff --git a/dlls/winebth.sys/unixlib.h b/dlls/winebth.sys/unixlib.h new file mode 100644 index 00000000000..177faf4e855 --- /dev/null +++ b/dlls/winebth.sys/unixlib.h @@ -0,0 +1,116 @@ +/* + * Unix interface definitions + * + * Copyright 2023 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_UNIXLIB_H +#define __WINE_WINEBLUETOOTH_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" + +#define BLUETOOTH_ADAPTER_PROP_ROLES ( 1 ) +#define BLUETOOTH_ADAPTER_PROP_DISCOVERABLE ( 1 << 2 ) +#define BLUETOOTH_ADAPTER_PROP_PAIRABLE ( 1 << 3 ) +#define BLUETOOTH_ADAPTER_PROP_POWERED ( 1 << 4 ) +#define BLUETOOTH_ADAPTER_PROP_ADDRESS ( 1 << 5 ) +#define BLUETOOTH_ADAPTER_PROP_NAME ( 1 << 6 ) +#define BLUETOOTH_ADAPTER_PROP_MANUFACTURER ( 1 << 7 ) +#define BLUETOOTH_ADAPTER_PROP_CLASS ( 1 << 8 ) +#define BLUETOOTH_ADAPTER_PROP_DISCOVERING ( 1 << 9 ) +#define BLUETOOTH_ADAPTER_PROP_VERSION ( 1 << 10 ) + +#define BLUETOOTH_ADAPTER_ROLE_CENTRAL ( 1 ) +#define BLUETOOTH_ADAPTER_ROLE_PERIPHERAL ( 1 << 2 ) +#define BLUETOOTH_ADAPTER_ROLE_CENTRAL_PERIPHERAL ( 1 << 3 ) + +#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_adapter_get_unique_name_params +{ + unix_name_t adapter; + + char *buf; + SIZE_T buf_size; +}; + +struct bluetooth_device_get_unique_name_params +{ + unix_name_t device; + + char *buf; + SIZE_T buf_size; +}; + +struct bluetooth_watcher_init_params +{ + unix_handle_t unix_watcher_ctx; +}; + +struct bluetooth_watcher_close_params +{ + unix_handle_t unix_watcher_ctx; +}; + +struct bluetooth_event_loop_once_params +{ + unix_handle_t unix_watcher_ctx; + + struct winebluetooth_event_loop_result result; +}; + +enum bluetoothapis_funcs +{ + unix_bluetooth_init, + unix_bluetooth_shutdown, + + unix_bluetooth_adapter_get_unique_name, + unix_bluetooth_adapter_free, + + unix_bluetooth_watcher_init, + unix_bluetooth_watcher_close, + + unix_bluetooth_event_loop_once, + + unix_funcs_count +}; + +#define UNIX_BLUETOOTH_CALL( func, params ) WINE_UNIX_CALL( unix_##func, params ) + +#endif /* __WINE_WINEBLUETOOTH_UNIXLIB_H */ diff --git a/dlls/winebth.sys/unixlib_priv.h b/dlls/winebth.sys/unixlib_priv.h new file mode 100644 index 00000000000..0690e86f99b --- /dev/null +++ b/dlls/winebth.sys/unixlib_priv.h @@ -0,0 +1,55 @@ +/* + * Bluetoothapis Unix interface + * + * Copyright 2023 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_PRIV_H +#define __WINE_BLUETOOTHAPIS_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 + +struct unix_name +{ + char *str; + SIZE_T refcnt; + + struct wine_rb_entry entry; +}; + +extern struct unix_name *unix_name_get_or_create( const char *str ); +extern void unix_name_free( struct unix_name *name ); + +extern void *bluez_dbus_init( void ); +extern void bluez_dbus_close( void *connection ); +extern void bluez_dbus_free( void *connection ); +extern NTSTATUS bluez_dbus_loop( void *connection, void *watcher_ctx, struct winebluetooth_event_loop_result *result ); +extern NTSTATUS bluez_watcher_init( void *connection, void **ctx ); +extern void bluez_watcher_close( void *connection, void *ctx ); +#endif /* SONAME_LIBDBUS_1 */ +#endif /* __WINE_BLUETOOTHAPIS_UNIXLIB_PRIV_H */ diff --git a/dlls/winebth.sys/winebluetooth.c b/dlls/winebth.sys/winebluetooth.c new file mode 100644 index 00000000000..88bfb656c18 --- /dev/null +++ b/dlls/winebth.sys/winebluetooth.c @@ -0,0 +1,114 @@ +/* + * 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" + +WINE_DEFAULT_DEBUG_CHANNEL( winebluetooth ); + +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", 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 }; + TRACE( "(%p)\n", 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", watcher.handle ); + return UNIX_BLUETOOTH_CALL( bluetooth_watcher_close, &args ); +} + +NTSTATUS winebluetooth_event_loop_run( winebluetooth_watcher_t watcher, + struct winebluetooth_event_loop_result *result ) +{ + struct bluetooth_event_loop_once_params params = {0}; + NTSTATUS status; + + TRACE( "(%p, %p, %p)\n", watcher, result ); + + params.unix_watcher_ctx = watcher.handle; + status = UNIX_BLUETOOTH_CALL( bluetooth_event_loop_once, ¶ms ); + *result = params.result; + return status; +} + +NTSTATUS winebluetooth_init( void ) +{ + NTSTATUS status; + + 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..0cf90620ba8 --- /dev/null +++ b/dlls/winebth.sys/winebluetooth.h @@ -0,0 +1,147 @@ +/* + * 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__ + +#include <bthsdpdef.h> +#include <bluetoothapis.h> + +typedef struct +{ +#ifdef WINE_UNIX_LIB + void *handle; +#else + UINT_PTR handle; +#endif +} winebluetooth_radio_t; + +typedef UINT16 winebluetooth_radio_props_mask_t; + +#define WINEBLUTOOTH_RADIO_PROPERTY_NAME (1) +#define WINEBLUETOOTH_RADIO_PROPERTY_ADDRESS (1 << 2) +#define WINEBLUETOOTH_RADIO_PROPERTY_DISCOVERABLE (1 << 3) +#define WINEBLUETOOTH_RADIO_PROPERTY_CONNECTABLE (1 << 4) +#define WINEBLUETOOTH_RADIO_PROPERTY_CLASS (1 << 5) +#define WINEBLUETOOTH_RADIO_PROPERTY_MANUFACTURER (1 << 6) +#define WINEBLUETOOTH_RADIO_PROPERTY_VERSION (1 << 7) +#define WINEBLUETOOTH_RADIO_PROPERTY_DISCOVERING (1 << 8) +#define WINEBLUETOOTH_RADIO_PROPERTY_PAIRABLE (1 << 9) + +#define WINEBLUETOOTH_RADIO_ALL_PROPERTIES \ + (WINEBLUTOOTH_RADIO_PROPERTY_NAME | WINEBLUETOOTH_RADIO_PROPERTY_ADDRESS | \ + WINEBLUETOOTH_RADIO_PROPERTY_DISCOVERABLE | WINEBLUETOOTH_RADIO_PROPERTY_CONNECTABLE | \ + WINEBLUETOOTH_RADIO_PROPERTY_CLASS | WINEBLUETOOTH_RADIO_PROPERTY_MANUFACTURER | \ + WINEBLUETOOTH_RADIO_PROPERTY_VERSION | WINEBLUETOOTH_RADIO_PROPERTY_DISCOVERING | \ + WINEBLUETOOTH_RADIO_PROPERTY_PAIRABLE) + +union winebluetooth_property +{ + BOOL bool; + ULONG ulong; + BLUETOOTH_ADDRESS address; + WCHAR name[BLUETOOTH_MAX_NAME_SIZE]; +}; + +struct winebluetooth_bluetooth_version +{ + UCHAR major; + UCHAR minor; +}; + +struct winebluetooth_radio_properties +{ + BOOL discoverable; + BOOL connectable; + BOOL discovering; + BOOL pairable; + BLUETOOTH_ADDRESS address; + CHAR name[BLUETOOTH_MAX_NAME_SIZE]; + ULONG class; + USHORT manufacturer; + struct winebluetooth_bluetooth_version version; +}; + +NTSTATUS winebluetooth_radio_get_unique_name( winebluetooth_radio_t radio, char *name, + SIZE_T *size ); +void winebluetooth_radio_free( winebluetooth_radio_t radio ); +static inline BOOL winebluetooth_radio_equal( winebluetooth_radio_t r1, winebluetooth_radio_t r2 ) +{ + return r1.handle == r2.handle; +} + +typedef struct +{ +#ifdef WINE_UNIX_LIB + void *handle; +#else + UINT_PTR handle; +#endif +} winebluetooth_watcher_t; + +enum winebluetooth_watcher_event_type +{ + BLUETOOTH_WATCHER_EVENT_TYPE_NONE, + BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED, + BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_REMOVED, +}; + +struct winebluetooth_watcher_event_radio_added +{ + winebluetooth_radio_props_mask_t props_mask; + struct winebluetooth_radio_properties props; + winebluetooth_radio_t radio; +}; + +union winebluetooth_watcher_event_data +{ + struct winebluetooth_watcher_event_radio_added radio_added; + winebluetooth_radio_t radio_removed; +}; + +struct winebluetooth_watcher_event +{ + enum winebluetooth_watcher_event_type event_type; + union winebluetooth_watcher_event_data event_data; +}; + +NTSTATUS winebluetooth_watcher_init( winebluetooth_watcher_t *watcher ); +NTSTATUS winebluetooth_watcher_close( winebluetooth_watcher_t watcher ); + +enum winebluetooth_event_loop_status +{ + WINEBLUETOOTH_EVENT_LOOP_STATUS_WATCHER_EVENT, +}; + +struct winebluetooth_event_loop_result +{ + enum winebluetooth_event_loop_status status; + union { + struct winebluetooth_watcher_event watcher_event; + } data; +}; + +NTSTATUS winebluetooth_event_loop_run( winebluetooth_watcher_t watcher, + struct winebluetooth_event_loop_result *result ); +NTSTATUS winebluetooth_init( void ); +NTSTATUS winebluetooth_shutdown( void ); + +#endif /* __WINE_WINEBLUETOOTH_H__ */ diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index 699c9915e27..9be19a5d42a 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -27,13 +27,18 @@ #include <windef.h> #include <winbase.h> #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>
#include "winebth_priv.h" +#include "winebluetooth.h"
WINE_DEFAULT_DEBUG_CHANNEL( winebth );
@@ -41,6 +46,233 @@ 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; + DEVICE_OBJECT *device_fdo_bus; + winebluetooth_radio_props_mask_t props_mask; + struct winebluetooth_radio_properties props; + winebluetooth_radio_t radio; + WCHAR *hw_name; + UNICODE_STRING bthport_symlink_name; + UNICODE_STRING bthradio_symlink_name; + 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 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; + DEVICE_OBJECT *device_obj; + 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", 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", status ); + return; + } + + 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; + device->hw_name = hw_name; + device->device_fdo_bus = NULL; + device->props = event.props; + device->props_mask = event.props_mask; + 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_bluetooth_radio( winebluetooth_radio_t radio ) +{ + struct bluetooth_radio *device; + + EnterCriticalSection(&device_list_cs); + LIST_FOR_EACH_ENTRY(device, &device_list, struct bluetooth_radio, entry) + { + if (winebluetooth_radio_equal(radio, device->radio) && !device->removed) + { + TRACE( "Removing bluetooth radio %p\n", radio.handle ); + device->removed = TRUE; + list_remove( &device->entry ); + IoInvalidateDeviceRelations( device->device_obj, BusRelations ); + break; + } + } + LeaveCriticalSection( &device_list_cs ); + + IoInvalidateDeviceRelations( bus_pdo, BusRelations ); + winebluetooth_radio_free( radio ); +} + +static void remove_pending_irps(struct bluetooth_radio *device) +{ + LIST_ENTRY *entry; + IRP *irp; + + while ((entry = RemoveHeadList( &device->irp_list )) != &device->irp_list) + { + irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); + irp->IoStatus.Status = STATUS_DELETE_PENDING; + irp->IoStatus.Information = 0; + IoCompleteRequest(irp, IO_NO_INCREMENT); + } +} + +static DWORD CALLBACK bluetooth_event_loop_thread_proc( void *arg ) +{ + NTSTATUS status; + while (TRUE) + { + struct winebluetooth_event_loop_result result = {0}; + + status = winebluetooth_event_loop_run( watcher, &result ); + if (status != STATUS_PENDING) break; + + switch (result.status) + { + case WINEBLUETOOTH_EVENT_LOOP_STATUS_WATCHER_EVENT: + { + struct winebluetooth_watcher_event *event = &result.data.watcher_event; + switch (event->event_type) + { + case BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED: + add_bluetooth_radio( event->event_data.radio_added ); + break; + case BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_REMOVED: + remove_bluetooth_radio( event->event_data.radio_removed ); + break; + default: + FIXME( "Unknown bluetooth watcher event code: %#x\n", event->event_type ); + } + 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 ); @@ -50,8 +282,41 @@ 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) + { + break; + } + + EnterCriticalSection( &device_list_cs ); + devices = ExAllocatePool( + PagedPool, offsetof( DEVICE_RELATIONS, Objects[list_count( &device_list )] ) ); + if (devices == NULL) + { + LeaveCriticalSection( &device_list_cs ); + irp->IoStatus.Status = STATUS_NO_MEMORY; + break; + } + + LIST_FOR_EACH_ENTRY(radio, &device_list, struct bluetooth_radio, entry) + { + devices->Objects[i++] = radio->device_obj; + call_fastcall_func1( ObfReferenceObject, radio->device_obj ); + } + LeaveCriticalSection( &device_list_cs ); + + devices->Count = i; + irp->IoStatus.Information = (ULONG_PTR)devices; + irp->IoStatus.Status = STATUS_SUCCESS; break; + } case IRP_MN_START_DEVICE: + event_loop_thread = + CreateThread( NULL, 0, bluetooth_event_loop_thread_proc, NULL, 0, NULL ); irp->IoStatus.Status = STATUS_SUCCESS; break; case IRP_MN_SURPRISE_REMOVAL: @@ -59,7 +324,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 ); @@ -74,17 +352,74 @@ 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 BusQueryDeviceID: + append_id( &buf, L"WINEBTH\%s", ext->hw_name ); + break; + case BusQueryInstanceID: + append_id(&buf, L"%p", ext->radio); + break; + case BusQueryHardwareIDs: + append_id( &buf, L"WINEBTH\%s", ext->hw_name ); + case BusQueryCompatibleIDs: + append_id( &buf, L"WINEBTH\WINE_COMP_BTH" ); + append_id( &buf, L"" ); + 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_DEVICE_RELATIONS: + { + DEVICE_RELATIONS *devices; + if (stack->Parameters.QueryDeviceRelations.Type != BusRelations) + { + break; + } + + devices = ExAllocatePool( PagedPool, offsetof( DEVICE_RELATIONS, Objects[1] ) ); + if (devices == NULL) + { + irp->IoStatus.Status = STATUS_NO_MEMORY; + break; + } + + devices->Count = 0; + if (!device->removed) + { + devices->Objects[0] = device->device_fdo_bus; + call_fastcall_func1( ObfReferenceObject, device->device_fdo_bus ); + devices->Count++; + } + + irp->IoStatus.Information = (ULONG_PTR)devices; + ret = STATUS_SUCCESS; break; + } case IRP_MN_QUERY_ID: + ret = query_id( device, irp, stack->Parameters.QueryId.IdType ); break; case IRP_MN_QUERY_CAPABILITIES: { @@ -96,17 +431,76 @@ static NTSTATUS WINAPI pdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) break; } case IRP_MN_START_DEVICE: + { + union + { + UINT64 uint; + BYTE addr[8]; + } radio_addr; + + if (device->props_mask & WINEBLUETOOTH_RADIO_PROPERTY_ADDRESS) + { + memset( &radio_addr, 0, sizeof( radio_addr ) ); + memcpy( &radio_addr.addr[2], device->props.address.rgBytes, + sizeof( device->props.address.rgBytes ) ); + IoSetDevicePropertyData( device_obj, &DEVPKEY_BluetoothRadio_Address, + LOCALE_NEUTRAL, 0, DEVPROP_TYPE_UINT64, + sizeof( radio_addr ), &radio_addr ); + } + if (device->props_mask & WINEBLUETOOTH_RADIO_PROPERTY_MANUFACTURER) + { + UINT16 manufacturer = device->props.manufacturer; + IoSetDevicePropertyData( device_obj, &DEVPKEY_BluetoothRadio_Manufacturer, + LOCALE_NEUTRAL, 0, DEVPROP_TYPE_UINT16, + sizeof( manufacturer ), &manufacturer ); + } + + if (IoRegisterDeviceInterface( device_obj, &GUID_BTHPORT_DEVICE_INTERFACE, NULL, + &device->bthport_symlink_name ) == STATUS_SUCCESS) + IoSetDeviceInterfaceState( &device->bthport_symlink_name, TRUE ); + + 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 ); + irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest( irp, IO_NO_INCREMENT ); return STATUS_SUCCESS; } case IRP_MN_SURPRISE_REMOVAL: + { + remove_pending_irps( device ); + EnterCriticalSection( &device_list_cs ); + if ( !device->removed ) + { + device->removed = TRUE; + list_remove( &device->entry ); + } + LeaveCriticalSection( &device_list_cs ); ret = STATUS_SUCCESS; - break; + } }
irp->IoStatus.Status = ret; @@ -146,13 +540,24 @@ 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; + + status = winebluetooth_watcher_init( &watcher ); + if ( status != STATUS_SUCCESS ) + { + ERR( "Failed to initialize bluetooth watcher: %#lx\n", status ); + 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 ef29ed2f8c0..e73586fead3 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
diff --git a/include/Makefile.in b/include/Makefile.in index d4f63fe169c..a62a24d327f 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..067e9cac59a --- /dev/null +++ b/include/ddk/bthguid.h @@ -0,0 +1,57 @@ +/* + * 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 ); + +#define DEFINE_BTH_RADIO_DEVPROPKEY( d, i ) \ + DEFINE_DEVPROPKEY( DEVPKEY_BluetoothRadio_##d, 0xa92f26ca, 0xeda7, 0x4b1d, 0x9d, 0xb2, 0x27, \ + 0xb6, 0x8a, 0xa5, 0xa2, 0xeb, ( i ) ) + +DEFINE_BTH_RADIO_DEVPROPKEY( Address, 1 ); /* DEVPROP_TYPE_UINT64 */ +DEFINE_BTH_RADIO_DEVPROPKEY( Manufacturer, 2 ); /* DEVPROP_TYPE_UINT16 */ +DEFINE_BTH_RADIO_DEVPROPKEY( LMPSupportedFeatures, 3 ); /* DEVPROP_TYPE_UINT64 */ +DEFINE_BTH_RADIO_DEVPROPKEY( LMPVersion, 4 ); /* DEVPROP_TYPE_BYTE */ +DEFINE_BTH_RADIO_DEVPROPKEY( HCIVendorFeatures, 8 ); /* DEVPROP_TYPE_UINT64 */ +DEFINE_BTH_RADIO_DEVPROPKEY( MaximumAdvertisementDataLength, 17 ); /* DEVPROP_TYPE_UINT16 */ +DEFINE_BTH_RADIO_DEVPROPKEY( LELocalSupportedFeatures, 22 ); /* DEVPROP_TYPE_UINT64 */ + + +#ifdef WINE_BTH_EXTENSIONS +DEFINE_GUID( GUID_BLUETOOTH_WINE_AUTH_REQUEST, 0x1a188a9b, 0x2d33, 0x4269, 0x85, 0xea, 0xd5, 0x3d, + 0x69, 0x18, 0xdc, 0xeb ); + +#define DEFINE_WINEBTH_RADIO_DEVPROPKEY( d, i ) \ + DEFINE_DEVPROPKEY( DEVPKEY_WineBluetooth_Radio_##d, 0x9ccd2c1a, 0x4b06, 0x4ecf, 0xb1, 0xd5, \ + 0x35, 0x62, 0xd6, 0xb0, 0xda, 0xac, (i) ) + +/* Authentication I/O capability as currently advertised by the radio. DEVPROP_TYPE_BYTE */ +DEFINE_WINEBTH_RADIO_DEVPROPKEY( Capability, 1 ); +#endif + +#undef DEFINE_BTH_RADIO_DEVPROPKEY + +#endif
From: Vibhav Pant vibhavp@gmail.com
--- dlls/winebth.sys/bthenum.c | 713 +++++++++++++++++++++++++++++++ dlls/winebth.sys/dbus.c | 632 +++++++++++++++++++++++++++ dlls/winebth.sys/unixlib.c | 9 + dlls/winebth.sys/unixlib.h | 18 +- dlls/winebth.sys/winebluetooth.c | 9 + dlls/winebth.sys/winebluetooth.h | 114 +++++ dlls/winebth.sys/winebth.c | 82 ++++ dlls/winebth.sys/winebth_priv.h | 65 +++ include/Makefile.in | 1 + include/bthdef.h | 23 + include/bthioctl.h | 71 +++ include/ddk/bthguid.h | 27 ++ 12 files changed, 1760 insertions(+), 4 deletions(-) create mode 100644 dlls/winebth.sys/bthenum.c create mode 100644 include/bthioctl.h
diff --git a/dlls/winebth.sys/bthenum.c b/dlls/winebth.sys/bthenum.c new file mode 100644 index 00000000000..2de12b334ae --- /dev/null +++ b/dlls/winebth.sys/bthenum.c @@ -0,0 +1,713 @@ +/* + * Bluetooth enumerator 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> + +#define WINE_BTH_EXTENSIONS + +#include <ntstatus.h> +#define WIN32_NO_STATUS +#include <windef.h> +#include <winbase.h> +#include <winternl.h> +#include <winuser.h> +#include <devpkey.h> +#include <dbt.h> +#include <bthsdpdef.h> +#include <bluetoothapis.h> +#include <bthdef.h> +#include <bthioctl.h> +#include <ddk/wdm.h> +#include <ddk/bthguid.h> +#include <winreg.h> +#include <cfgmgr32.h> +#include <wine/debug.h> +#include <wine/list.h> +#include <wine/rbtree.h> + +#include "winebth_priv.h" +#include "winebluetooth.h" + +#include <initguid.h> +DEFINE_GUID( GUID_NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); + +WINE_DEFAULT_DEBUG_CHANNEL( bthenum ); + +static DRIVER_OBJECT *driver_obj; + +/* winebluetooth device property flags that are used to construct the 'flags' field in + * BTH_DEVICE_INFO */ +#define BTH_DEVICE_INFO_FLAGS_MEMBERS \ + ( WINEBLUETOOTH_DEVICE_PROPERTY_ADDRESS | WINEBLUETOOTH_DEVICE_PROPERTY_CLASS | \ + WINEBLUETOOTH_DEVICE_PROPERTY_NAME | WINEBLUETOOTH_DEVICE_PROPERTY_TRUSTED | \ + WINEBLUETOOTH_DEVICE_PROPERTY_PAIRED | WINEBLUETOOTH_DEVICE_PROPERTY_CONNECTED | \ + WINEBLUETOOTH_DEVICE_PROPERTY_LEGACY_PAIRING_HINT ) + +enum bthenum_phys_device_kind +{ + BTHENUM_PHYS_DEVICE_KIND_REMOTE_RADIO, +}; + +struct device_ext +{ + BOOL is_fdo; + BOOL removed; +}; + +struct bthenum_func_local_radio_ext +{ + struct device_ext base; + DEVICE_OBJECT *parent_radio_pdo; + DEVICE_OBJECT *device_obj; + + RTL_CRITICAL_SECTION cs; + /* Remote bluetooth radios associated with this radio (struct bthenum_phys_remote_radio) */ + struct list devices; +}; + +struct phys_device_ext +{ + struct device_ext base; + + enum bthenum_phys_device_kind kind; + struct bthenum_func_local_radio_ext *parent_fdo; +}; + +/* Child PDO under a bluetooth radio FDO bus */ +struct bthenum_phys_remote_radio_ext +{ + struct phys_device_ext base; + struct list entry; + + DEVICE_OBJECT *device_obj; + winebluetooth_device_t remote_radio_handle; + winebluetooth_device_props_mask_t known_props; + struct winebluetooth_device_properties props; + + UNICODE_STRING device_link_name; +}; + +static struct bthenum_func_local_radio_ext *fdo_from_DEVICE_OBJECT( DEVICE_OBJECT *device ) +{ + struct device_ext *impl = (struct device_ext *)device->DeviceExtension; + + if (impl->is_fdo) return CONTAINING_RECORD( impl, struct bthenum_func_local_radio_ext, base ); + return CONTAINING_RECORD( impl, struct phys_device_ext, base )->parent_fdo; +} + +NTSTATUS WINAPI bthenum_device_added( DEVICE_OBJECT *fdo_bus, + struct winebluetooth_watcher_event_device_added event ) +{ + struct bthenum_func_local_radio_ext *fdo; + struct bthenum_phys_remote_radio_ext *child_device; + DEVICE_OBJECT *child_pdo; + NTSTATUS status; + + TRACE("fdo_bus=%p\n", fdo_bus); + + fdo = fdo_from_DEVICE_OBJECT( fdo_bus ); + + status = IoCreateDevice( fdo_bus->DriverObject, sizeof( struct bthenum_phys_remote_radio_ext ), + NULL, FILE_DEVICE_BLUETOOTH, 0, FALSE, &child_pdo ); + if (status != STATUS_SUCCESS) + { + ERR( "failed to create bluetooth radio device: %#lx\n", status ); + return status; + } + child_device = child_pdo->DeviceExtension; + child_device->base.base.is_fdo = FALSE; + child_device->base.base.removed = FALSE; + child_device->base.kind = BTHENUM_PHYS_DEVICE_KIND_REMOTE_RADIO; + child_device->base.parent_fdo = fdo; + + child_device->remote_radio_handle = event.device; + child_device->props = event.props; + child_device->known_props = event.known_props_mask; + child_device->device_obj = child_pdo; + + RtlEnterCriticalSection( &fdo->cs ); + list_add_tail(&fdo->devices, &child_device->entry); + RtlLeaveCriticalSection( &fdo->cs ); + + IoInvalidateDeviceRelations( fdo_bus, BusRelations ); + + return STATUS_SUCCESS; +} + +NTSTATUS WINAPI bthenum_device_removed( DEVICE_OBJECT *fdo_bus, + const winebluetooth_device_t device_handle ) +{ + struct bthenum_func_local_radio_ext *fdo; + struct bthenum_phys_remote_radio_ext *remote_device; + + fdo = fdo_from_DEVICE_OBJECT( fdo_bus ); + + RtlEnterCriticalSection( &fdo->cs ); + LIST_FOR_EACH_ENTRY( remote_device, &fdo->devices, struct bthenum_phys_remote_radio_ext, entry ) + { + if (winebluetooth_device_equal( device_handle, remote_device->remote_radio_handle ) + && !remote_device->base.base.removed) + { + remote_device->base.base.removed = TRUE; + list_remove( &remote_device->entry ); + break; + } + } + RtlLeaveCriticalSection( &fdo->cs ); + + IoInvalidateDeviceRelations( fdo_bus, BusRelations ); + return STATUS_SUCCESS; +} + +static UINT32 device_get_flags( winebluetooth_device_props_mask_t props_mask, + struct winebluetooth_device_properties props ); + +static BOOL device_set_properties( DEVICE_OBJECT *device_obj, UINT32 *old_flags, + winebluetooth_device_props_mask_t props_mask, + struct winebluetooth_device_properties props ); + +static void device_refresh_properties( struct bthenum_phys_remote_radio_ext *remote_device, + struct winebluetooth_device_properties new_props, + winebluetooth_device_props_mask_t changed_props) +{ + UINT32 old_flags = device_get_flags( remote_device->known_props, remote_device->props ); + device_set_properties( remote_device->device_obj, &old_flags, changed_props, new_props ); + + return; +} + +NTSTATUS +bthenum_device_props_changed( DEVICE_OBJECT *fdo_bus, + struct winebluetooth_watcher_event_device_props_changed event ) +{ + struct bthenum_func_local_radio_ext *fdo; + struct bthenum_phys_remote_radio_ext *remote_device; + + TRACE( "fdo_bus=%p\n", fdo_bus ); + + fdo = fdo_from_DEVICE_OBJECT( fdo_bus ); + + RtlEnterCriticalSection( &fdo->cs ); + LIST_FOR_EACH_ENTRY( remote_device, &fdo->devices, struct bthenum_phys_remote_radio_ext, entry ) + { + if (winebluetooth_device_equal( remote_device->remote_radio_handle, event.device )) + { + device_refresh_properties( remote_device, event.prop, event.changed_props_mask ); + } + } + RtlLeaveCriticalSection( &fdo->cs ); + + return STATUS_SUCCESS; +} + +/* Called once for every Bluetooth Radio discovered. */ +NTSTATUS WINAPI bthenum_create_fdo_bus( DEVICE_OBJECT *pdo, DEVICE_OBJECT **fdo_bus ) +{ + struct bthenum_func_local_radio_ext *fdo; + NTSTATUS status; + + TRACE("(%p, %p)\n", pdo, fdo_bus); + + status = IoCreateDevice( driver_obj, sizeof( struct bthenum_func_local_radio_ext ), NULL, + FILE_DEVICE_BUS_EXTENDER, 0, FALSE, fdo_bus ); + if (status != STATUS_SUCCESS) + { + ERR( "failed to create bus FDO, status: %#lx\n", status ); + return status; + } + + fdo = (*fdo_bus)->DeviceExtension; + fdo->base.is_fdo = TRUE; + fdo->base.removed = FALSE; + fdo->parent_radio_pdo = pdo; + fdo->device_obj = *fdo_bus; + list_init( &fdo->devices ); + + RtlInitializeCriticalSectionEx( &fdo->cs, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO ); + fdo->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": bthenum_func_local_radio.cs"); + + IoInvalidateDeviceRelations(*fdo_bus, BusRelations); + (*fdo_bus)->Flags &= ~DO_DEVICE_INITIALIZING; + return STATUS_SUCCESS; +} + +static UINT32 device_get_flags( winebluetooth_device_props_mask_t props_mask, + struct winebluetooth_device_properties props ) +{ + UINT32 flags = 0; + + if (props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_ADDRESS) flags |= BDIF_ADDRESS; + if (props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_CLASS) flags |= BDIF_COD; + if (props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_NAME) flags |= BDIF_NAME; + if (props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_TRUSTED && props.trusted) flags |= BDIF_PERSONAL; + if (props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_PAIRED && props.paired) flags |= BDIF_PAIRED; + if (props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_CONNECTED && props.connected) flags |= BDIF_CONNECTED; + if (!(props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_LEGACY_PAIRING_HINT)) + { + flags |= BDIF_SSP_SUPPORTED | BDIF_SSP_MITM_PROTECTED; + if (flags & BDIF_PAIRED) flags |= BDIF_SSP_PAIRED; + } + + return flags; +} + +static BOOL device_set_properties( DEVICE_OBJECT *device_obj, UINT32 *old_flags, + winebluetooth_device_props_mask_t props_mask, + struct winebluetooth_device_properties props ) +{ + UINT32 flags = device_get_flags( props_mask, props ); + BOOL flags_changed = (!old_flags || *old_flags != flags); + + if (props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_ADDRESS) + { + WCHAR addr_str[13]; + BYTE *addr = props.address.rgBytes; + + swprintf( addr_str, ARRAY_SIZE( addr_str ), L"%02X%02X%02X%02X%02X%02X", addr[0], addr[1], + addr[2], addr[3], addr[4], addr[5] ); + IoSetDevicePropertyData( device_obj, &DEVPKEY_Bluetooth_DeviceAddress, LOCALE_NEUTRAL, + PLUGPLAY_PROPERTY_PERSISTENT, DEVPROP_TYPE_STRING, + sizeof( addr_str ), addr_str ); + } + if (props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_NAME) + { + SIZE_T chars = mbstowcs( NULL, props.name, 0 ); + SIZE_T size = sizeof( WCHAR ) * ( chars + 1 ); + WCHAR *buf = malloc( size ); + if ( buf ) + { + mbstowcs( buf, props.name, strlen( props.name ) + 1 ); + IoSetDevicePropertyData( device_obj, &DEVPKEY_WineBluetooth_Device_Name, LOCALE_NEUTRAL, + PLUGPLAY_PROPERTY_PERSISTENT, DEVPROP_TYPE_STRING, size, buf ); + free( buf ); + } + } + if (props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_PNPID) + { + struct winebluetooth_device_pnpid pnpid = props.pnpid; + IoSetDevicePropertyData( device_obj, &DEVPKEY_Bluetooth_DeviceVIDSource, LOCALE_NEUTRAL, + PLUGPLAY_PROPERTY_PERSISTENT, DEVPROP_TYPE_BYTE, + sizeof( pnpid.vid_source ), &pnpid.vid_source ); + IoSetDevicePropertyData( device_obj, &DEVPKEY_Bluetooth_DeviceVID, LOCALE_NEUTRAL, + PLUGPLAY_PROPERTY_PERSISTENT, DEVPROP_TYPE_UINT16, + sizeof( pnpid.vid ), &pnpid.vid ); + IoSetDevicePropertyData( device_obj, &DEVPKEY_Bluetooth_DevicePID, LOCALE_NEUTRAL, + PLUGPLAY_PROPERTY_PERSISTENT, DEVPROP_TYPE_UINT16, + sizeof( pnpid.pid ), &pnpid.pid ); + IoSetDevicePropertyData( device_obj, &DEVPKEY_Bluetooth_DeviceProductVersion, + LOCALE_NEUTRAL, PLUGPLAY_PROPERTY_PERSISTENT, DEVPROP_TYPE_UINT16, + sizeof( pnpid.product_version ), &pnpid.product_version ); + } + if (props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_CLASS) + { + UINT32 cod = props.class; + IoSetDevicePropertyData( device_obj, &DEVPKEY_Bluetooth_ClassOfDevice, LOCALE_NEUTRAL, + PLUGPLAY_PROPERTY_PERSISTENT, DEVPROP_TYPE_UINT32, sizeof( cod ), + &cod ); + } + if (flags_changed) + IoSetDevicePropertyData( device_obj, &DEVPKEY_Bluetooth_DeviceFlags, LOCALE_NEUTRAL, + PLUGPLAY_PROPERTY_PERSISTENT, DEVPROP_TYPE_UINT32, sizeof( flags ), + &flags ); + + return flags_changed; +} + +static void device_get_properties( DEVICE_OBJECT *device_obj, BTH_DEVICE_INFO *info ) +{ + NTSTATUS status; + UINT32 flags; + + TRACE( "%p\n", device_obj ); + + memset( info, 0, sizeof( *info ) ); + status = IoGetDevicePropertyData( device_obj, &DEVPKEY_Bluetooth_DeviceFlags, LOCALE_NEUTRAL, + PLUGPLAY_PROPERTY_PERSISTENT, sizeof( flags ), &flags, NULL, + NULL ); + if (status != STATUS_SUCCESS) + WARN( "could not get device flags: %#x\n", status); + + info->flags = flags; + + if (flags & BDIF_ADDRESS) + { + WCHAR addr_buf[13]; + BLUETOOTH_ADDRESS addr = {0}; + + status = IoGetDevicePropertyData( device_obj, &DEVPKEY_Bluetooth_DeviceAddress, + LOCALE_NEUTRAL, PLUGPLAY_PROPERTY_PERSISTENT, + sizeof( addr_buf ), addr_buf, NULL, NULL ); + if (status != STATUS_SUCCESS) + { + WARN( "could not get device address: %#x\n", status ); + info->flags &= ~BDIF_ADDRESS; + } + else + swscanf( addr_buf, L"%x%x%x%x%x%x", addr.rgBytes[0], addr.rgBytes[1], addr.rgBytes[2], + addr.rgBytes[3], addr.rgBytes[4], addr.rgBytes[5] ); + info->address = addr.ullLong; + } + if (flags & BDIF_COD) + { + UINT32 cod; + status = IoGetDevicePropertyData( device_obj, &DEVPKEY_Bluetooth_ClassOfDevice, + LOCALE_NEUTRAL, PLUGPLAY_PROPERTY_PERSISTENT, + sizeof( cod ), &cod, NULL, NULL ); + if (status != STATUS_SUCCESS) + { + WARN( "could not get device class: %#x\n", status ); + info->flags &= ~BDIF_COD; + } + else info->classOfDevice = cod; + } + if (flags & BDIF_NAME) + { + WCHAR name_buf[BLUETOOTH_MAX_NAME_SIZE]; + + status = IoGetDevicePropertyData( device_obj, &DEVPKEY_WineBluetooth_Device_Name, + LOCALE_NEUTRAL, PLUGPLAY_PROPERTY_PERSISTENT, + sizeof( name_buf ), name_buf, NULL, NULL ); + if (status != STATUS_SUCCESS) + { + WARN("could not get device name: %#x\n", status); + info->flags &= BDIF_NAME; + } + else + { + wcstombs(info->name, name_buf, sizeof(info->name)); + info->name[ARRAY_SIZE(info->name) - 1] = '\0'; + } + } +} + +static NTSTATUS fdo_query_id( const DEVICE_OBJECT *device_obj, IRP *irp, BUS_QUERY_ID_TYPE type, + struct string_buffer *buf ) +{ + TRACE( "device_obj=%p irp=%p type=%d buf=%p\n", device_obj, irp, + debugstr_BUS_QUERY_ID_TYPE( type ), buf ); + switch ( type ) + { + case BusQueryDeviceID: + append_id( buf, L"BTH\WINE_BTHBRB" ); + break; + case BusQueryInstanceID: + append_id(buf, L"%p", device_obj); + break; + case BusQueryHardwareIDs: + append_id( buf, L"BTH\WINE_BTHBRB" ); + case BusQueryCompatibleIDs: + append_id( buf, L"" ); + break; + default: + FIXME( "Unhandled ID query type %s.\n", debugstr_BUS_QUERY_ID_TYPE( type ) ); + return irp->IoStatus.Status; + } + if (buf->string == NULL) irp->IoStatus.Status = STATUS_NO_MEMORY; + else + { + irp->IoStatus.Status = STATUS_SUCCESS; + irp->IoStatus.Information = (ULONG_PTR)buf->string; + } + return irp->IoStatus.Status; +} + +static NTSTATUS WINAPI fdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) +{ + IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp ); + struct bthenum_func_local_radio_ext *device = + (struct bthenum_func_local_radio_ext *)device_obj->DeviceExtension; + + TRACE( "device_obj %p, irp %p, code %s\n", device_obj, irp, + debugstr_minor_function_code( stack->MinorFunction ) ); + + switch (stack->MinorFunction) + { + case IRP_MN_QUERY_DEVICE_RELATIONS: + { + struct bthenum_phys_remote_radio_ext *remote; + DEVICE_RELATIONS *devices; + SIZE_T i = 0; + SIZE_T objects_count; + + if (stack->Parameters.QueryDeviceRelations.Type != BusRelations) + { + break; + } + + RtlEnterCriticalSection( &device->cs ); + objects_count = list_count( &device->devices ); + devices = ExAllocatePool( + PagedPool, offsetof( DEVICE_RELATIONS, Objects[objects_count] ) ); + if (devices == NULL) + { + RtlLeaveCriticalSection( &device->cs ); + irp->IoStatus.Status = STATUS_NO_MEMORY; + break; + } + + LIST_FOR_EACH_ENTRY( remote, &device->devices, struct bthenum_phys_remote_radio_ext, entry ) + { + devices->Objects[i++] = remote->device_obj; + call_fastcall_func1( ObfReferenceObject, remote->device_obj ); + } + RtlLeaveCriticalSection( &device->cs ); + assert( objects_count == i ); + devices->Count = i; + irp->IoStatus.Information = (ULONG_PTR)devices; + irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(irp, IO_NO_INCREMENT); + return STATUS_SUCCESS; + break; + } + case IRP_MN_START_DEVICE: + irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(irp, IO_NO_INCREMENT); + return irp->IoStatus.Status; + case IRP_MN_SURPRISE_REMOVAL: + { + irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest( irp, IO_NO_INCREMENT ); + return STATUS_SUCCESS; + } + case IRP_MN_REMOVE_DEVICE: + { + irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest( irp, IO_NO_INCREMENT ); + IoDeleteDevice( device->parent_radio_pdo ); + return STATUS_SUCCESS; + } + case IRP_MN_QUERY_CAPABILITIES: + { + break; + } + case IRP_MN_QUERY_ID: + { + BUS_QUERY_ID_TYPE type = stack->Parameters.QueryId.IdType; + struct string_buffer buf = { 0 }; + + irp->IoStatus.Status = fdo_query_id( device_obj, irp, type, &buf ); + IoCompleteRequest( irp, IO_NO_INCREMENT ); + return irp->IoStatus.Status; + } + default: + FIXME( "Unhandled minor function %s\n", debugstr_minor_function_code( stack->MinorFunction ) ); + } + + IoSkipCurrentIrpStackLocation( irp ); + return IoCallDriver( device->parent_radio_pdo, irp ); +} + +static NTSTATUS pdo_device_query_id( const struct bthenum_phys_remote_radio_ext *device, IRP *irp, + BUS_QUERY_ID_TYPE type, struct string_buffer *buf ) +{ + TRACE( "device=%p irp=%p type=%s buf=%p\n", device, irp, debugstr_BUS_QUERY_ID_TYPE( type ), buf ); + switch (type) + { + case BusQueryDeviceID: + case BusQueryHardwareIDs: + append_id( buf, L"BTHENUM\Dev_%02X%02X%02X%02X%02X%02X", + device->props.address.rgBytes[0], device->props.address.rgBytes[1], + device->props.address.rgBytes[2], device->props.address.rgBytes[3], + device->props.address.rgBytes[4], device->props.address.rgBytes[5] ); + if ( type == BusQueryHardwareIDs ) append_id( buf, L"" ); + break; + case BusQueryInstanceID: + append_id( buf, L"%p&BluetoothDevice_%02X%02X%02X%02X%02X%02X", + device->remote_radio_handle, device->props.address.rgBytes[0], + device->props.address.rgBytes[1], device->props.address.rgBytes[2], + device->props.address.rgBytes[3], device->props.address.rgBytes[4], + device->props.address.rgBytes[5] ); + break; + case BusQueryCompatibleIDs: + append_id( buf, L"BTHENUM\GENERIC_DEVICE" ); + append_id( buf, L"" ); + break; + default: + FIXME( "Unhandled ID query type %s.\n", debugstr_BUS_QUERY_ID_TYPE( type ) ); + return irp->IoStatus.Status; + } + if (buf->string == NULL) irp->IoStatus.Status = STATUS_NO_MEMORY; + else + { + irp->IoStatus.Status = STATUS_SUCCESS; + irp->IoStatus.Information = (ULONG_PTR)buf->string; + } + return irp->IoStatus.Status; +} + +static NTSTATUS WINAPI pdo_device_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) +{ + struct bthenum_phys_remote_radio_ext *device = + (struct bthenum_phys_remote_radio_ext *)device_obj->DeviceExtension; + IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp ); + UCHAR code = stack->MinorFunction; + NTSTATUS status = irp->IoStatus.Status; + + TRACE( "device %p, irp %p, code %s\n", device, irp, debugstr_minor_function_code( code ) ); + switch (code) + { + case IRP_MN_START_DEVICE: + { + GUID g = GUID_NULL; + + if ( IoRegisterDeviceInterface( device->device_obj, &GUID_BTH_DEVICE_INTERFACE, NULL, + &device->device_link_name ) == STATUS_SUCCESS ) + IoSetDeviceInterfaceState( &device->device_link_name, TRUE ); + IoSetDevicePropertyData( device_obj, &DEVPKEY_Bluetooth_ServiceGUID, LOCALE_NEUTRAL, 0, + DEVPROP_TYPE_GUID, sizeof( GUID_NULL ), &g ); + device_set_properties( device_obj, NULL, device->known_props, device->props ); + break; + } + case IRP_MN_SURPRISE_REMOVAL: + if (!device->base.base.removed) + { + device->base.base.removed = TRUE; + list_remove( &device->entry ); + } + + status = STATUS_SUCCESS; + break; + case IRP_MN_REMOVE_DEVICE: + assert( device->base.base.removed ); + winebluetooth_device_free( device->remote_radio_handle ); + if (device->device_link_name.Buffer) RtlFreeUnicodeString( &device->device_link_name ); + IoDeleteDevice( device_obj ); + + irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest( irp, IO_NO_INCREMENT ); + return STATUS_SUCCESS; + case IRP_MN_QUERY_ID: + { + BUS_QUERY_ID_TYPE type = stack->Parameters.QueryId.IdType; + struct string_buffer buf = { 0 }; + + status = pdo_device_query_id( device, irp, type, &buf ); + break; + } + case IRP_MN_QUERY_CAPABILITIES: + { + DEVICE_CAPABILITIES *caps = stack->Parameters.DeviceCapabilities.Capabilities; + caps->Removable = TRUE; + caps->SurpriseRemovalOK = TRUE; + caps->RawDeviceOK = TRUE; + status = STATUS_SUCCESS; + break; + } + } + irp->IoStatus.Status = status; + IoCompleteRequest( irp, IO_NO_INCREMENT ); + return status; +} + +#define PNPID_FORMAT "VID&%04x%04x_PID&%04x" +#define PNPID_FORMAT_ARGS(p) (p).vid_source, (p).vid, (p).pid + +#define LOCALMFG_FORMAT "LOCALMFG&%04x" + +static NTSTATUS WINAPI pdo_pnp( DEVICE_OBJECT *device, IRP *irp ) +{ + struct phys_device_ext *base = (struct phys_device_ext *)device->DeviceExtension; + switch (base->kind) + { + case BTHENUM_PHYS_DEVICE_KIND_REMOTE_RADIO: + return pdo_device_pnp( device, irp ); + DEFAULT_UNREACHABLE; + } +} + +static NTSTATUS WINAPI bthenum_pnp( DEVICE_OBJECT *device, IRP *irp ) +{ + struct device_ext *base = (struct device_ext *)device->DeviceExtension; + if (base->is_fdo) return fdo_pnp( device, irp ); + return pdo_pnp(device, irp); +} + +static NTSTATUS WINAPI bthenum_ioctl( DEVICE_OBJECT *device, IRP *irp ) +{ + IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp ); + IO_STATUS_BLOCK *iosb = &irp->IoStatus; + struct bthenum_func_local_radio_ext *fdo = fdo_from_DEVICE_OBJECT( device ); + ULONG insize = stack->Parameters.DeviceIoControl.InputBufferLength; + ULONG outsize = stack->Parameters.DeviceIoControl.OutputBufferLength; + ULONG code = stack->Parameters.DeviceIoControl.IoControlCode; + NTSTATUS status; + + TRACE( "device %p irp % code %s", device, irp, debugstr_IoControlCode( code ) ); + switch ( code ) + { + case IOCTL_BTH_GET_DEVICE_INFO: + { + BTH_DEVICE_INFO_LIST *list = (BTH_DEVICE_INFO_LIST *)irp->AssociatedIrp.SystemBuffer; + struct bthenum_phys_remote_radio_ext *remote_device; + BOOL have_buf = TRUE; + + if (list == NULL) + { + status = STATUS_INVALID_PARAMETER; + break; + } + if (insize < sizeof(*list)) + { + status = STATUS_INVALID_BUFFER_SIZE; + break; + } + memset(list, 0, sizeof(*list)); + + iosb->Information = sizeof( BTH_DEVICE_INFO_LIST ); + + RtlEnterCriticalSection( &fdo->cs ); + LIST_FOR_EACH_ENTRY( remote_device, &fdo->devices, struct bthenum_phys_remote_radio_ext, entry ) + { + SIZE_T i = list->numOfDevices++; + have_buf = have_buf && outsize >= ( sizeof( BTH_DEVICE_INFO_LIST ) + + sizeof( BTH_DEVICE_INFO ) * i ); + if (have_buf) + { + device_get_properties( remote_device->device_obj, &list->deviceList[i] ); + if (i > 0) + iosb->Information += sizeof( BTH_DEVICE_INFO ); + } + } + RtlLeaveCriticalSection( &fdo->cs ); + status = have_buf ? STATUS_SUCCESS : STATUS_INVALID_BUFFER_SIZE; + break; + } + default: + IoSkipCurrentIrpStackLocation( irp ); + return IoCallDriver( fdo->parent_radio_pdo, irp ); + } + + iosb->Status = status; + IoCompleteRequest( irp, IO_NO_INCREMENT ); + return status; +} + + +NTSTATUS WINAPI bthenum_entry_point( DRIVER_OBJECT *driver, UNICODE_STRING *path ) +{ + TRACE( "(%p, %s)\n", driver, debugstr_w( path->Buffer ) ); + + driver_obj = driver; + driver->MajorFunction[IRP_MJ_PNP] = bthenum_pnp; + driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = bthenum_ioctl; + return STATUS_SUCCESS; +} diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index 4878d9dc8a4..1809cfc6cf5 100644 --- a/dlls/winebth.sys/dbus.c +++ b/dlls/winebth.sys/dbus.c @@ -75,6 +75,8 @@ const int bluez_timeout = -1; DBUS_TYPE_OBJECT_PATH_AS_STRING \ DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING
+#define DBUS_PROPERTIES_SIGNAL_PROPERTIESCHANGED "PropertiesChanged" + #define DBUS_PROPERTIES_CHANGED_SIGNATURE \ DBUS_TYPE_STRING_AS_STRING \ DBUS_TYPE_ARRAY_AS_STRING \ @@ -85,6 +87,7 @@ const int bluez_timeout = -1;
#define BLUEZ_DEST "org.bluez" #define BLUEZ_INTERFACE_ADAPTER "org.bluez.Adapter1" +#define BLUEZ_INTERFACE_DEVICE "org.bluez.Device1"
#define DO_FUNC( f ) typeof( f ) (*p_##f) DBUS_FUNCS; @@ -111,6 +114,11 @@ failed: return FALSE; }
+static inline int starts_with( const char *str, const char *prefix ) +{ + return !strncmp( str, prefix, strlen( prefix ) ); +} + static NTSTATUS bluez_dbus_error_to_ntstatus( const DBusError *error ) {
@@ -318,6 +326,271 @@ static void bluez_radio_prop_from_dict_entry( const char *prop_name, DBusMessage } }
+static void bluez_device_prop_from_dict_entry( const char *prop_name, DBusMessageIter *variant, + BOOL get_device_reported_name, + struct winebluetooth_device_properties *props, + winebluetooth_device_props_mask_t *props_mask, + winebluetooth_device_props_mask_t wanted_props_mask ) +{ + + TRACE_( dbus ) + ( "(%s, %p, %d, %p, %p, %#x)\n", debugstr_a( prop_name ), variant, get_device_reported_name, props, + props_mask, wanted_props_mask ); + + if ( wanted_props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_ADDRESS && + !strcmp( prop_name, "Address" ) && + p_dbus_message_iter_get_arg_type( variant ) == DBUS_TYPE_STRING ) + { + const char *addr_str; + + p_dbus_message_iter_get_basic( variant, &addr_str ); + parse_mac_address( addr_str, props->address.rgBytes ); + *props_mask |= WINEBLUETOOTH_DEVICE_PROPERTY_ADDRESS; + } + if ( wanted_props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_ADDRESS_TYPE && + !strcmp( prop_name, "AddressType" ) && + p_dbus_message_iter_get_arg_type( variant ) == DBUS_TYPE_STRING ) + { + const char *address_type; + + p_dbus_message_iter_get_basic( variant, &address_type ); + props->address_type = WINEBLUETOOTH_DEVICE_ADDRESS_TYPE_UNKNOWN; + if (!strcmp( address_type, "random" )) + { + props->address_type = WINEBLUETOOTH_DEVICE_ADDRESS_TYPE_RANDOM; + } + if (!strcmp( address_type, "public" )) + { + props->address_type = WINEBLUETOOTH_DEVICE_ADDRESS_TYPE_PUBLIC; + } + *props_mask |= WINEBLUETOOTH_DEVICE_PROPERTY_ADDRESS_TYPE; + } + else if ( wanted_props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_CLASS && + !strcmp( prop_name, "Class" ) && + p_dbus_message_iter_get_arg_type( variant ) == DBUS_TYPE_UINT32 ) + { + dbus_uint32_t class; + + p_dbus_message_iter_get_basic( variant, &class ); + props->class = class; + *props_mask |= WINEBLUETOOTH_DEVICE_PROPERTY_CLASS; + } + else if ( wanted_props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_CONNECTED && + !strcmp( prop_name, "Connected" ) && + p_dbus_message_iter_get_arg_type( variant ) == DBUS_TYPE_BOOLEAN ) + { + dbus_bool_t connected; + + p_dbus_message_iter_get_basic( variant, &connected ); + props->connected = connected ? TRUE : FALSE; + *props_mask |= WINEBLUETOOTH_DEVICE_PROPERTY_CONNECTED; + } + else if ( wanted_props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_BONDED && + !strcmp( prop_name, "Bonded" ) && + p_dbus_message_iter_get_arg_type( variant ) == DBUS_TYPE_BOOLEAN ) + { + dbus_bool_t bonded; + + p_dbus_message_iter_get_basic( variant, &bonded ); + props->bonded = bonded ? TRUE : FALSE; + *props_mask |= WINEBLUETOOTH_DEVICE_PROPERTY_BONDED; + } + else if ( wanted_props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_PAIRED && + !strcmp( prop_name, "Paired" ) && + p_dbus_message_iter_get_arg_type( variant ) == DBUS_TYPE_BOOLEAN ) + { + dbus_bool_t paired; + + p_dbus_message_iter_get_basic( variant, &paired ); + props->paired = paired ? TRUE : FALSE; + *props_mask |= WINEBLUETOOTH_DEVICE_PROPERTY_PAIRED; + } + else if ( wanted_props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_LEGACY_PAIRING_HINT && + !strcmp( prop_name, "LegacyPairing" ) && + p_dbus_message_iter_get_arg_type( variant ) == DBUS_TYPE_BOOLEAN ) + { + dbus_bool_t legacy_pairing; + + p_dbus_message_iter_get_basic( variant, &legacy_pairing ); + props->legacy_pairing_hint = legacy_pairing ? TRUE : FALSE; + *props_mask |= WINEBLUETOOTH_DEVICE_PROPERTY_LEGACY_PAIRING_HINT; + } + else if ( wanted_props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_TRUSTED && + !strcmp( prop_name, "Trusted" ) && + p_dbus_message_iter_get_arg_type( variant ) == DBUS_TYPE_BOOLEAN ) + { + dbus_bool_t trusted; + + p_dbus_message_iter_get_basic( variant, &trusted ); + props->trusted = trusted ? TRUE : FALSE; + *props_mask |= WINEBLUETOOTH_DEVICE_PROPERTY_TRUSTED; + } + else if ( wanted_props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_NAME && + !get_device_reported_name && !strcmp( prop_name, "Alias" ) && + p_dbus_message_iter_get_arg_type( variant ) == DBUS_TYPE_STRING ) + { + const char *alias = NULL; + SIZE_T len; + + p_dbus_message_iter_get_basic( variant, &alias ); + assert( alias != NULL ); + len = strlen( alias ); + memcpy( props->name, alias, min( len + 1, ARRAYSIZE( props->name ) ) ); + props->name[ARRAYSIZE( props->name ) - 1] = '\0'; + *props_mask |= WINEBLUETOOTH_DEVICE_PROPERTY_NAME; + } + else if ( wanted_props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_NAME && + get_device_reported_name && !strcmp( prop_name, "Name" ) && + p_dbus_message_iter_get_arg_type( variant ) == DBUS_TYPE_STRING ) + { + const char *name = NULL; + SIZE_T len; + + p_dbus_message_iter_get_basic( variant, &name ); + assert( name != NULL ); + len = strlen( name ); + memcpy( props->name, name, min( len + 1, ARRAYSIZE( props->name ) ) ); + props->name[ARRAYSIZE( props->name ) - 1] = '\0'; + *props_mask |= WINEBLUETOOTH_DEVICE_PROPERTY_NAME; + } + else if (wanted_props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_PNPID && + !strcmp( prop_name, "Modalias" ) && + p_dbus_message_iter_get_arg_type( variant ) == DBUS_TYPE_STRING ) + { + const char *modalias = NULL; + + p_dbus_message_iter_get_basic( variant, &modalias ); + assert( modalias != NULL ); + + if (strstr(modalias, "bluetooth:")) + { + props->pnpid.vid_source = 0x0001; + modalias += strlen("bluetooth:"); + } + else if (strstr(modalias, "usb:")) + { + props->pnpid.vid_source = 0x0002; + modalias += strlen("usb:"); + } + sscanf( modalias, "v%04hXp%04hXd%04hX", &props->pnpid.vid, &props->pnpid.pid, + &props->pnpid.product_version ); + *props_mask |= WINEBLUETOOTH_DEVICE_PROPERTY_PNPID; + } +} + +NTSTATUS +bluez_device_props_get_all_reply_to_params( + DBusMessage *reply, struct bluetooth_get_device_props_by_path_params *params ) +{ + DBusMessageIter dict, prop_iter, variant; + const char *prop_name; + + p_dbus_message_iter_init( reply, &dict ); + if (p_dbus_message_iter_get_arg_type( &dict ) != DBUS_TYPE_ARRAY) + { + ERR( "unexpected result type from GetAll\n" ); + p_dbus_message_unref( reply ); + return STATUS_INTERNAL_ERROR; + } + params->valid_props = 0; + + p_dbus_message_iter_recurse( &dict, &prop_iter ); + while ((prop_name = bluez_next_dict_entry( &prop_iter, &variant ))) + { + winebluetooth_device_props_mask_t prop = 0; + + bluez_device_prop_from_dict_entry( prop_name, &variant, params->get_device_reported_name, + ¶ms->props, &prop, + WINEBLUETOOTH_DEVICE_ALL_PROPERTIES ); + params->valid_props |= prop; + } + + p_dbus_message_unref( reply ); + return STATUS_SUCCESS; +} + +struct bluez_device_get_all_reply_cb_data +{ + struct bluetooth_get_device_props_by_path_params params; + void (*callback)( NTSTATUS status, struct bluetooth_get_device_props_by_path_params *params, void *data ); + void *user_data; +}; + +static void bluez_device_get_all_reply_callback( DBusPendingCall *pending, void *user_data ) +{ + struct bluez_device_get_all_reply_cb_data *cb_data; + DBusMessage *reply; + DBusError error; + NTSTATUS status; + + TRACE( "(%p, %p)\n", pending, user_data ); + + cb_data = user_data; + assert( cb_data != NULL ); + assert( cb_data->callback != NULL ); + + reply = p_dbus_pending_call_steal_reply( pending ); + p_dbus_error_init( &error ); + if (p_dbus_set_error_from_message(&error, reply )) + { + status = bluez_dbus_error_to_ntstatus( &error ); + p_dbus_error_free( &error ); + } + else + { + status = bluez_device_props_get_all_reply_to_params( reply, &cb_data->params ); + } + p_dbus_message_unref( reply ); + p_dbus_pending_call_unref( pending ); + cb_data->callback( status, &cb_data->params, cb_data->user_data ); +} + +static NTSTATUS bluez_device_get_props_by_path_async( + DBusConnection *connection, struct bluetooth_get_device_props_by_path_params *params, + DBusPendingCall **pending_call, + void ( *callback )( NTSTATUS status, struct bluetooth_get_device_props_by_path_params *params, + void *data ), + void *data ) +{ + DBusMessage *request; + DBusError error; + static const char *adapter_iface = BLUEZ_INTERFACE_DEVICE; + dbus_bool_t ret; + struct bluez_device_get_all_reply_cb_data *cb_data; + + request = p_dbus_message_new_method_call( BLUEZ_DEST, params->device_path->str, + DBUS_INTERFACE_PROPERTIES, "GetAll" ); + if (!request) return STATUS_NO_MEMORY; + + p_dbus_message_append_args( request, DBUS_TYPE_STRING, &adapter_iface, DBUS_TYPE_INVALID ); + p_dbus_error_init( &error ); + TRACE( "Getting all properties for device '%s'\n", params->device_path->str ); + ret = p_dbus_connection_send_with_reply( connection, request, pending_call, bluez_timeout ); + p_dbus_message_unref( request ); + if (!ret) return STATUS_NO_MEMORY; + if (*pending_call == NULL) return STATUS_INTERNAL_ERROR; + + if (callback != NULL) + { + cb_data = calloc( 1, sizeof( struct bluez_device_get_all_reply_cb_data ) ); + if (cb_data == NULL) return STATUS_NO_MEMORY; + + memcpy( &cb_data->params, params, + sizeof( struct bluetooth_get_device_props_by_path_params ) ); + cb_data->callback = callback; + cb_data->user_data = data; + + if (!p_dbus_pending_call_set_notify( *pending_call, bluez_device_get_all_reply_callback, + cb_data, free )) + { + free( cb_data ); + return STATUS_NO_MEMORY; + } + } + + return STATUS_SUCCESS; +} + struct bluez_pair_complete_params { void *user_data; @@ -374,6 +647,20 @@ void bluez_dbus_free( void *connection ) p_dbus_connection_unref( connection ); }
+static struct unix_name *bluez_device_path_get_adapter( const char *device_path ) +{ + static const char *adapter_prefix = "/org/bluez/hci"; + const char *adapter_end; + char buf[80]; + + if (!starts_with( device_path, "/org/bluez/hci" )) return NULL; + adapter_end = strchr( &device_path[strlen( adapter_prefix )-1], '/' ); + if (adapter_end == NULL) return NULL; + memcpy (buf, device_path, adapter_end - device_path); + buf[adapter_end - device_path] = '\0'; + return unix_name_get_or_create( buf ); +} + struct bluez_watcher_event_pending_call { struct list entry; @@ -427,6 +714,41 @@ static BOOL bluez_event_list_queue_new_event( struct list *event_list, return bluez_event_list_queue_new_event_with_call( event_list, event_type, event, NULL, NULL ); }
+static void bluez_filter_device_props_changed_callback( DBusPendingCall *call, void *user_data ) +{ + union winebluetooth_watcher_event_data *event = user_data; + struct winebluetooth_watcher_event_device_props_changed *changed = &event->device_props_changed; + const struct unix_name *device = event->device_props_changed.device.handle; + DBusMessage *reply = p_dbus_pending_call_steal_reply( call ); + DBusMessageIter dict, prop_iter, variant; + const char *prop_name; + DBusError error; + + TRACE( "call %p, device %s radio %s\n", call, debugstr_a(changed->device.handle), debugstr_a(changed->radio.handle) ); + + p_dbus_error_init( &error ); + if (p_dbus_set_error_from_message(&error, reply)) + { + ERR( "Failed to get device properties for '%s': '%s': '%s'\n", device->str, error.name, + error.message ); + p_dbus_error_free( &error ); + p_dbus_message_unref( reply ); + return; + } + p_dbus_error_free( &error ); + + p_dbus_message_iter_init( reply, &dict ); + p_dbus_message_iter_recurse( &dict, &prop_iter ); + while((prop_name = bluez_next_dict_entry( &prop_iter, &variant ))) + { + bluez_device_prop_from_dict_entry( prop_name, &variant, TRUE, &changed->prop, + &changed->changed_props_mask, + changed->invalid_props_mask ); + } + changed->invalid_props_mask &= ~changed->changed_props_mask; + p_dbus_message_unref( reply ); +} + static DBusHandlerResult bluez_filter( DBusConnection *conn, DBusMessage *msg, void *user_data ) { struct list *event_list; @@ -493,6 +815,64 @@ static DBusHandlerResult bluez_filter( DBusConnection *conn, DBusMessage *msg, v } } } + else if (strcmp( iface_name, BLUEZ_INTERFACE_DEVICE ) == 0) + { + struct winebluetooth_watcher_event_device_added device_added = {0}; + DBusMessageIter props_iter, variant; + const char *prop_name; + BOOL success = FALSE; + struct unix_name *radio = NULL; + + p_dbus_message_iter_next( &iface_entry ); + p_dbus_message_iter_recurse( &iface_entry, &props_iter ); + + while((prop_name = bluez_next_dict_entry( &props_iter, &variant ))) + { + + if (!strcmp( prop_name, "Adapter" ) + && p_dbus_message_iter_get_arg_type( &variant ) == DBUS_TYPE_OBJECT_PATH) + { + const char *path; + p_dbus_message_iter_get_basic( &variant, &path ); + device_added.radio.handle = radio = unix_name_get_or_create( path ); + if (!device_added.radio.handle) + { + ERR( "failed to allocate memory for adapter path %s\n", path ); + break; + } + success = TRUE; + } + bluez_device_prop_from_dict_entry( + prop_name, &variant, TRUE, &device_added.props, + &device_added.known_props_mask, WINEBLUETOOTH_DEVICE_ALL_PROPERTIES ); + } + if (success) + { + struct unix_name *device; + + assert(radio != NULL); + device_added.device.handle = device = unix_name_get_or_create( object_path );; + if (!device) + { + + ERR( "failed to allocate memory for device path %s\n", object_path ); + unix_name_free( device ); + unix_name_free( radio ); + } + else + { + union winebluetooth_watcher_event_data event = { .device_added = device_added }; + TRACE( "New BlueZ %s object added at %s: %p\n", BLUEZ_INTERFACE_DEVICE, + object_path, device_added.device.handle ); + if (!bluez_event_list_queue_new_event( + event_list, BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_ADDED, event )) + { + unix_name_free( device ); + unix_name_free( radio ); + } + } + } + } p_dbus_message_iter_next( &ifaces_iter ); } } @@ -538,9 +918,159 @@ static DBusHandlerResult bluez_filter( DBusConnection *conn, DBusMessage *msg, v unix_name_free(radio.handle); } } + else if (strcmp( interfaces[i], BLUEZ_INTERFACE_DEVICE ) == 0) + { + struct unix_name *radio; + struct unix_name *device = unix_name_get_or_create( object_path ); + struct winebluetooth_watcher_event_device_removed device_removed; + union winebluetooth_watcher_event_data event; + + if (!device) + { + ERR( "failed to allocate memory for device path %s\n", object_path ); + continue; + } + radio = bluez_device_path_get_adapter( object_path ); + if (!radio) + { + ERR( "failed to get associated radio for device path %s\n", object_path ); + unix_name_free( device ); + continue; + } + + device_removed.device.handle = device; + device_removed.radio.handle = radio; + event.device_removed = device_removed; + + if (!bluez_event_list_queue_new_event( + event_list, BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_REMOVED, event )) + { + unix_name_free( device ); + unix_name_free( radio ); + } + } } p_dbus_free_string_array( interfaces ); } + else if (p_dbus_message_is_signal( msg, DBUS_INTERFACE_PROPERTIES, DBUS_PROPERTIES_SIGNAL_PROPERTIESCHANGED ) && + p_dbus_message_has_signature( msg, DBUS_PROPERTIES_CHANGED_SIGNATURE )) + { + DBusMessageIter iter; + const char *iface; + + p_dbus_message_iter_init( msg, &iter ); + p_dbus_message_iter_get_basic( &iter, &iface ); + assert( iface != NULL ); + + if (strcmp( iface, BLUEZ_INTERFACE_DEVICE ) == 0) + { + struct winebluetooth_watcher_event_device_props_changed props_changed = {0}; + struct unix_name *device, *radio; + DBusPendingCall *pending_call = NULL; + DBusMessageIter changed_props_iter, invalid_props_iter, variant; + const char *prop_name; + const static struct + { + const char *prop_str; + winebluetooth_device_props_mask_t prop_mask; + } device_props_mask_map[] = { + { "Address", WINEBLUETOOTH_DEVICE_PROPERTY_ADDRESS }, + { "AddressType", WINEBLUETOOTH_DEVICE_PROPERTY_ADDRESS_TYPE }, + { "Class", WINEBLUETOOTH_DEVICE_PROPERTY_CLASS }, + { "Connected", WINEBLUETOOTH_DEVICE_PROPERTY_CONNECTED }, + { "Trusted", WINEBLUETOOTH_DEVICE_PROPERTY_TRUSTED }, + { "Paired", WINEBLUETOOTH_DEVICE_PROPERTY_PAIRED }, + { "Bonded", WINEBLUETOOTH_DEVICE_PROPERTY_BONDED }, + { "Name", WINEBLUETOOTH_DEVICE_PROPERTY_NAME }, + { "LegacyPairing", WINEBLUETOOTH_DEVICE_PROPERTY_LEGACY_PAIRING_HINT }, + { "ServicesResolved", WINEBLUETOOTH_DEVICE_PROPERTY_SERVICES_RESOLVED }, + { "Modalias", WINEBLUETOOTH_DEVICE_PROPERTY_PNPID }, + }; + const char *object_path = p_dbus_message_get_path( msg ); + + device = unix_name_get_or_create( object_path ); + if (!device) + { + ERR( "failed to allocate memory for device path %s\n", object_path ); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + radio = bluez_device_path_get_adapter( object_path ); + if (!radio) + { + ERR( "failed to get associated radio for device path %s\n", object_path ); + unix_name_free( device ); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + p_dbus_message_iter_next( &iter ); + p_dbus_message_iter_recurse( &iter, &changed_props_iter ); + while ((prop_name = bluez_next_dict_entry( &changed_props_iter, &variant ))) + { + bluez_device_prop_from_dict_entry( prop_name, &variant, FALSE, &props_changed.prop, + &props_changed.changed_props_mask, + WINEBLUETOOTH_DEVICE_ALL_PROPERTIES ); + } + p_dbus_message_iter_next( &iter ); + p_dbus_message_iter_recurse( &iter, &invalid_props_iter ); + while (p_dbus_message_iter_has_next( &invalid_props_iter )) + { + const char *invalid_prop_name = NULL; + SIZE_T i; + + p_dbus_message_iter_get_basic( &invalid_props_iter, &invalid_prop_name ); + assert( invalid_prop_name != NULL ); + for (i = 0; i < ARRAY_SIZE( device_props_mask_map ); i++) + { + if (strcmp(device_props_mask_map[i].prop_str, invalid_prop_name) == 0) + props_changed.invalid_props_mask |= device_props_mask_map[i].prop_mask; + } + p_dbus_message_iter_next( &invalid_props_iter ); + } + + TRACE( "Properties changed for device %s radio %s, changed %#x, invalidated %#x\n", + debugstr_a( device->str ), debugstr_a( radio->str ), + props_changed.changed_props_mask, props_changed.invalid_props_mask ); + if ( props_changed.invalid_props_mask != 0 ) + { + struct bluetooth_get_device_props_by_path_params params = {0}; + union winebluetooth_watcher_event_data event = { .device_props_changed = props_changed }; + NTSTATUS status; + + params.device_path = device; + status = bluez_device_get_props_by_path_async( conn, ¶ms, &pending_call, NULL, + NULL ); + if (status != STATUS_SUCCESS) + { + ERR( "Failed to create async call to get device properties: %#x\n", (int)status ); + unix_name_free( radio ); + unix_name_free( device ); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + if ( !bluez_event_list_queue_new_event_with_call( + event_list, BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_PROPERTIES_CHANGED, event, + pending_call, bluez_filter_device_props_changed_callback ) ) + { + unix_name_free( device ); + unix_name_free( radio ); + p_dbus_pending_call_cancel( pending_call ); + p_dbus_pending_call_unref( pending_call ); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + } + else + { + union winebluetooth_watcher_event_data event = { .device_props_changed = props_changed }; + if (!bluez_event_list_queue_new_event( + event_list, BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_PROPERTIES_CHANGED, + event )) + { + unix_name_free( device ); + unix_name_free( radio ); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + } + } + }
final_count = list_count( event_list ); assert( final_count >= init_count ); @@ -621,6 +1151,7 @@ void bluez_watcher_close( void *connection, void *ctx ) struct bluez_init_entry { union { + struct winebluetooth_watcher_event_device_added device; struct winebluetooth_watcher_event_radio_added radio; } object; struct list entry; @@ -671,6 +1202,91 @@ static NTSTATUS bluez_build_initial_device_lists( DBusMessage *reply, struct lis init_device->object.radio.radio.handle ); break; } + else if (!strcmp( iface, BLUEZ_INTERFACE_DEVICE )) + { + BOOL bredr = FALSE, le = FALSE; + const char *prop_name; + DBusMessageIter variant; + struct bluez_init_entry *init_device = calloc( 1, sizeof( *init_device ) ); + struct unix_name *device_name; + if ( !init_device ) + { + status = STATUS_NO_MEMORY; + goto done; + } + + init_device->object.device.device.handle = device_name = unix_name_get_or_create( path ); + if ( !device_name ) + { + free( init_device ); + status = STATUS_NO_MEMORY; + goto done; + } + + while((prop_name = bluez_next_dict_entry( &prop_iter, &variant ))) + { + if (!strcmp( prop_name, "Adapter" ) + && p_dbus_message_iter_get_arg_type( &variant ) == DBUS_TYPE_OBJECT_PATH) + { + const char *path; + p_dbus_message_iter_get_basic( &variant, &path ); + init_device->object.device.radio.handle = unix_name_get_or_create( path ); + if (!init_device->object.device.radio.handle) + { + unix_name_free( device_name ); + free( init_device ); + status = STATUS_NO_MEMORY; + goto done; + } + continue; + } + bluez_device_prop_from_dict_entry( prop_name, &variant, TRUE, + &init_device->object.device.props, + &init_device->object.device.known_props_mask, + WINEBLUETOOTH_DEVICE_ALL_PROPERTIES ); + } + + if ( init_device->object.device.props.address_type == + WINEBLUETOOTH_DEVICE_ADDRESS_TYPE_RANDOM ) + le = TRUE; + else if (init_device->object.device.props.legacy_pairing_hint) + bredr = TRUE; + else + { + /* TODO: Devices with AddressType == "public" can be dual-mode or + * LE-only radios. However, BlueZ does not seem to have any way + * to determine the underlying connection type of the device. For now, + * we'll treat the device as being connected over both BREDR and LE.*/ + FIXME( "Could not determine the connection type for device %s, assuming it's " + "both BR/EDR and LE\n", + path ); + bredr = le = TRUE; + } + assert( le || bredr ); + + init_device->object.device.connection_type = + le ? ( bredr ? WINEBLUETOOTH_DEVICE_CONNECTION_TYPE_DUAL + : WINEBLUETOOTH_DEVICE_CONNECTION_TYPE_LE ) + : WINEBLUETOOTH_DEVICE_CONNECTION_TYPE_LE; + if (le) + { + TRACE( "Found BlueZ LE %s object '%s': (device=%p, radio=%p)\n", + BLUEZ_INTERFACE_DEVICE, device_name->str, + init_device->object.device.device.handle, + init_device->object.device.radio.handle ); + + } + if (bredr) + { + TRACE( "Found BlueZ BR/EDR LE %s object '%s' (device=%p, radio=%p)\n", + BLUEZ_INTERFACE_DEVICE, device_name->str, + init_device->object.device.device.handle, + init_device->object.device.radio.handle ); + } + list_add_tail( device_list, &init_device->entry ); + + break; + } } }
@@ -718,6 +1334,9 @@ NTSTATUS bluez_dbus_loop( void *c, void *watcher, { struct bluez_watcher_event *bluez_event;
+ /* The first two conditionals can probably be nested within a single boolean like + * 'init_objects_enumerated', but I have decided to not do that for now to keep this state + * machine simple to understand. */ if (!list_empty( &watcher_ctx->initial_radio_list )) { struct bluez_init_entry *radio = @@ -731,6 +1350,19 @@ NTSTATUS bluez_dbus_loop( void *c, void *watcher, p_dbus_connection_unref( connection ); return STATUS_PENDING; } + else if (!list_empty( &watcher_ctx->initial_device_list )) + { + struct bluez_init_entry *device = + bluez_init_entries_list_pop( &watcher_ctx->initial_device_list ); + struct winebluetooth_watcher_event *watcher_event = &result->data.watcher_event; + + result->status = WINEBLUETOOTH_EVENT_LOOP_STATUS_WATCHER_EVENT; + watcher_event->event_type = BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_ADDED; + watcher_event->event_data.device_added = device->object.device; + free( device ); + p_dbus_connection_unref( connection ); + return STATUS_PENDING; + } else if ((bluez_event = bluez_watcher_event_queue_ready( watcher_ctx ))) { struct winebluetooth_watcher_event *watcher_event = &result->data.watcher_event; diff --git a/dlls/winebth.sys/unixlib.c b/dlls/winebth.sys/unixlib.c index 56c51f5d76f..9deaa848975 100644 --- a/dlls/winebth.sys/unixlib.c +++ b/dlls/winebth.sys/unixlib.c @@ -139,6 +139,13 @@ static NTSTATUS bluetooth_adapter_get_unique_name( void *args ) return get_unique_name( params->adapter, params->buf, ¶ms->buf_size ); }
+static NTSTATUS bluetooth_device_free( void *args ) +{ + struct bluetooth_device_free_params *params = args; + unix_name_free( params->device ); + return STATUS_SUCCESS; +} + static NTSTATUS bluetooth_adapter_free( void *args ) { struct bluetooth_adapter_free_params *params = args; @@ -179,6 +186,8 @@ const unixlib_entry_t __wine_unix_call_funcs[] = { bluetooth_adapter_get_unique_name, bluetooth_adapter_free,
+ bluetooth_device_free, + bluetooth_watcher_init, bluetooth_watcher_close,
diff --git a/dlls/winebth.sys/unixlib.h b/dlls/winebth.sys/unixlib.h index 177faf4e855..e3e60411d3b 100644 --- a/dlls/winebth.sys/unixlib.h +++ b/dlls/winebth.sys/unixlib.h @@ -70,12 +70,20 @@ struct bluetooth_adapter_get_unique_name_params SIZE_T buf_size; };
-struct bluetooth_device_get_unique_name_params +struct bluetooth_get_device_props_by_path_params { - unix_name_t device; + unix_name_t device_path; + /* If true, get the device reported name instead of the device alias, which can be overridden by + * the user. */ + BOOL get_device_reported_name;
- char *buf; - SIZE_T buf_size; + winebluetooth_device_props_mask_t valid_props; + struct winebluetooth_device_properties props; +}; + +struct bluetooth_device_free_params +{ + unix_name_t device; };
struct bluetooth_watcher_init_params @@ -103,6 +111,8 @@ enum bluetoothapis_funcs unix_bluetooth_adapter_get_unique_name, unix_bluetooth_adapter_free,
+ unix_bluetooth_device_free, + unix_bluetooth_watcher_init, unix_bluetooth_watcher_close,
diff --git a/dlls/winebth.sys/winebluetooth.c b/dlls/winebth.sys/winebluetooth.c index 88bfb656c18..fc4cad3115b 100644 --- a/dlls/winebth.sys/winebluetooth.c +++ b/dlls/winebth.sys/winebluetooth.c @@ -64,6 +64,15 @@ void winebluetooth_radio_free( winebluetooth_radio_t radio ) UNIX_BLUETOOTH_CALL( bluetooth_adapter_free, &args ); }
+void winebluetooth_device_free( winebluetooth_device_t device ) +{ + struct bluetooth_device_free_params params = {0}; + TRACE( "(%p)\n", device.handle ); + + params.device = device.handle; + UNIX_BLUETOOTH_CALL( bluetooth_device_free, ¶ms ); +} + NTSTATUS winebluetooth_watcher_init( winebluetooth_watcher_t *watcher ) { struct bluetooth_watcher_init_params args = { 0 }; diff --git a/dlls/winebth.sys/winebluetooth.h b/dlls/winebth.sys/winebluetooth.h index 0cf90620ba8..97d978b9e0c 100644 --- a/dlls/winebth.sys/winebluetooth.h +++ b/dlls/winebth.sys/winebluetooth.h @@ -80,6 +80,53 @@ struct winebluetooth_radio_properties struct winebluetooth_bluetooth_version version; };
+enum winebluetooth_device_address_type +{ + WINEBLUETOOTH_DEVICE_ADDRESS_TYPE_UNKNOWN, + WINEBLUETOOTH_DEVICE_ADDRESS_TYPE_RANDOM, + WINEBLUETOOTH_DEVICE_ADDRESS_TYPE_PUBLIC +}; + +struct winebluetooth_device_pnpid +{ + BYTE vid_source; + UINT16 vid; + UINT16 pid; + UINT16 product_version; +}; + +struct winebluetooth_device_properties +{ + BLUETOOTH_ADDRESS address; + enum winebluetooth_device_address_type address_type; + ULONG class; + BOOL connected; + BOOL trusted; + BOOL paired; + BOOL bonded; + char name[BLUETOOTH_MAX_NAME_SIZE]; + BOOL legacy_pairing_hint; + BOOL services_resolved; + struct winebluetooth_device_pnpid pnpid; +}; + +enum winebluetooth_discovery_transport +{ + WINEBLUETOOTH_DISCOVERY_TRANSPORT_AUTO, + WINEBLUETOOTH_DISCOVERY_TRANSPORT_BR_EDR, + WINEBLUETOOTH_DISCOVERY_TRANSPORT_LE +}; + +typedef struct +{ +#ifdef WINE_UNIX_LIB + void *handle; +#else + UINT_PTR handle; +#endif +} winebluetooth_device_t; + + NTSTATUS winebluetooth_radio_get_unique_name( winebluetooth_radio_t radio, char *name, SIZE_T *size ); void winebluetooth_radio_free( winebluetooth_radio_t radio ); @@ -88,6 +135,13 @@ static inline BOOL winebluetooth_radio_equal( winebluetooth_radio_t r1, wineblue return r1.handle == r2.handle; }
+void winebluetooth_device_free( winebluetooth_device_t device ); +static inline BOOL winebluetooth_device_equal( winebluetooth_device_t d1, + winebluetooth_device_t d2 ) +{ + return d1.handle == d2.handle; +} + typedef struct { #ifdef WINE_UNIX_LIB @@ -102,6 +156,63 @@ enum winebluetooth_watcher_event_type BLUETOOTH_WATCHER_EVENT_TYPE_NONE, BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED, BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_REMOVED, + BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_ADDED, + BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_REMOVED, + BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_PROPERTIES_CHANGED, +}; + +#define WINEBLUETOOTH_DEVICE_PROPERTY_ADDRESS (1) +#define WINEBLUETOOTH_DEVICE_PROPERTY_ADDRESS_TYPE (1 << 2) +#define WINEBLUETOOTH_DEVICE_PROPERTY_CLASS (1 << 3) +#define WINEBLUETOOTH_DEVICE_PROPERTY_CONNECTED (1 << 4) +#define WINEBLUETOOTH_DEVICE_PROPERTY_TRUSTED (1 << 5) +#define WINEBLUETOOTH_DEVICE_PROPERTY_PAIRED (1 << 6) +#define WINEBLUETOOTH_DEVICE_PROPERTY_BONDED (1 << 7) +#define WINEBLUETOOTH_DEVICE_PROPERTY_NAME (1 << 8) +#define WINEBLUETOOTH_DEVICE_PROPERTY_LEGACY_PAIRING_HINT (1 << 9) +#define WINEBLUETOOTH_DEVICE_PROPERTY_SERVICES_RESOLVED (1 << 10) +#define WINEBLUETOOTH_DEVICE_PROPERTY_PNPID (1 << 11) + +#define WINEBLUETOOTH_DEVICE_ALL_PROPERTIES \ + ( WINEBLUETOOTH_DEVICE_PROPERTY_ADDRESS | WINEBLUETOOTH_DEVICE_PROPERTY_ADDRESS_TYPE | \ + WINEBLUETOOTH_DEVICE_PROPERTY_CLASS | WINEBLUETOOTH_DEVICE_PROPERTY_CONNECTED | \ + WINEBLUETOOTH_DEVICE_PROPERTY_TRUSTED | WINEBLUETOOTH_DEVICE_PROPERTY_PAIRED | \ + WINEBLUETOOTH_DEVICE_PROPERTY_BONDED | WINEBLUETOOTH_DEVICE_PROPERTY_NAME | \ + WINEBLUETOOTH_DEVICE_PROPERTY_LEGACY_PAIRING_HINT | \ + WINEBLUETOOTH_DEVICE_PROPERTY_SERVICES_RESOLVED | WINEBLUETOOTH_DEVICE_PROPERTY_PNPID ) + +enum winebluetooth_device_connection_type +{ + WINEBLUETOOTH_DEVICE_CONNECTION_TYPE_BREDR, + WINEBLUETOOTH_DEVICE_CONNECTION_TYPE_LE, + WINEBLUETOOTH_DEVICE_CONNECTION_TYPE_DUAL, +}; + +typedef UINT16 winebluetooth_device_props_mask_t; + +struct winebluetooth_watcher_event_device_added +{ + winebluetooth_device_props_mask_t known_props_mask; + struct winebluetooth_device_properties props; + enum winebluetooth_device_connection_type connection_type; + winebluetooth_device_t device; + winebluetooth_radio_t radio; +}; + +struct winebluetooth_watcher_event_device_removed +{ + winebluetooth_radio_t radio; + winebluetooth_device_t device; +}; + +struct winebluetooth_watcher_event_device_props_changed +{ + winebluetooth_device_props_mask_t changed_props_mask; + struct winebluetooth_device_properties prop; + + winebluetooth_device_props_mask_t invalid_props_mask; + winebluetooth_device_t device; + winebluetooth_radio_t radio; };
struct winebluetooth_watcher_event_radio_added @@ -115,6 +226,9 @@ union winebluetooth_watcher_event_data { struct winebluetooth_watcher_event_radio_added radio_added; winebluetooth_radio_t radio_removed; + struct winebluetooth_watcher_event_device_added device_added; + struct winebluetooth_watcher_event_device_removed device_removed; + struct winebluetooth_watcher_event_device_props_changed device_props_changed; };
struct winebluetooth_watcher_event diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index 9be19a5d42a..637d4933752 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -22,14 +22,18 @@ #include <stdlib.h> #include <assert.h>
+#define WINE_BTH_EXTENSIONS #include <ntstatus.h> #define WIN32_NO_STATUS #include <windef.h> #include <winbase.h> #include <winternl.h> +#include <bthsdpdef.h> +#include <bluetoothapis.h> #include <initguid.h> #include <devpkey.h> #include <bthdef.h> +#include <bthioctl.h> #include <winioctl.h> #include <ddk/wdm.h> #include <ddk/bthguid.h> @@ -79,6 +83,19 @@ struct bluetooth_radio LIST_ENTRY irp_list; };
+static NTSTATUS WINAPI dispatch_bluetooth(DEVICE_OBJECT *device, IRP *irp ) +{ + IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp ); + ULONG code = stack->Parameters.DeviceIoControl.IoControlCode; + NTSTATUS status = irp->IoStatus.Status; + + TRACE( "device %p irp %p code %s\n", device, irp, debugstr_IoControlCode( code ) ); + + irp->IoStatus.Status = status; + IoCompleteRequest( irp, IO_NO_INCREMENT ); + return status; +} + void WINAPIV append_id(struct string_buffer *buffer, const WCHAR *format, ...) { va_list args; @@ -150,6 +167,7 @@ static NTSTATUS radio_get_hw_name_w( winebluetooth_radio_t radio, WCHAR **name ) free( name_a ); return STATUS_SUCCESS; } + static void add_bluetooth_radio( struct winebluetooth_watcher_event_radio_added event ) { struct bluetooth_radio *device; @@ -193,6 +211,7 @@ static void add_bluetooth_radio( struct winebluetooth_watcher_event_radio_added list_add_tail( &device_list, &device->entry ); LeaveCriticalSection( &device_list_cs );
+ bthenum_create_fdo_bus( device_obj, &device->device_fdo_bus ); IoInvalidateDeviceRelations( bus_pdo, BusRelations ); }
@@ -218,6 +237,55 @@ static void remove_bluetooth_radio( winebluetooth_radio_t radio ) winebluetooth_radio_free( radio ); }
+static void add_remote_bluetooth_device( struct winebluetooth_watcher_event_device_added event ) +{ + struct bluetooth_radio *device; + + EnterCriticalSection( &device_list_cs ); + LIST_FOR_EACH_ENTRY( device, &device_list, struct bluetooth_radio, entry ) + { + if (winebluetooth_radio_equal( device->radio, event.radio )) + { + bthenum_device_added( device->device_fdo_bus, event ); + break; + } + } + LeaveCriticalSection( &device_list_cs ); +} + +static void +remove_remote_bluetooth_device( struct winebluetooth_watcher_event_device_removed event ) +{ + struct bluetooth_radio *device; + + EnterCriticalSection( &device_list_cs ); + LIST_FOR_EACH_ENTRY( device, &device_list, struct bluetooth_radio, entry ) + { + if (winebluetooth_radio_equal( device->radio, event.radio )) + { + bthenum_device_removed( device->device_fdo_bus, event.device ); + break; + } + } + LeaveCriticalSection( &device_list_cs ); +} + +static void remote_bluetooth_device_props_changed( + struct winebluetooth_watcher_event_device_props_changed event ) +{ + struct bluetooth_radio *device; + EnterCriticalSection( &device_list_cs ); + LIST_FOR_EACH_ENTRY(device, &device_list, struct bluetooth_radio, entry) + { + if (winebluetooth_radio_equal( device->radio, event.radio )) + { + bthenum_device_props_changed( device->device_fdo_bus, event ); + break; + } + } + LeaveCriticalSection( &device_list_cs ); +} + static void remove_pending_irps(struct bluetooth_radio *device) { LIST_ENTRY *entry; @@ -255,6 +323,16 @@ static DWORD CALLBACK bluetooth_event_loop_thread_proc( void *arg ) case BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_REMOVED: remove_bluetooth_radio( event->event_data.radio_removed ); break; + case BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_ADDED: + add_remote_bluetooth_device( event->event_data.device_added ); + break; + case BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_REMOVED: + remove_remote_bluetooth_device( event->event_data.device_removed ); + break; + case BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_PROPERTIES_CHANGED: + remote_bluetooth_device_props_changed( + event->event_data.device_props_changed ); + break; default: FIXME( "Unknown bluetooth watcher event code: %#x\n", event->event_type ); } @@ -541,6 +619,8 @@ static void WINAPI driver_unload( DRIVER_OBJECT *driver ) NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) { NTSTATUS status; + UNICODE_STRING driver_bthenum = RTL_CONSTANT_STRING( L"\Driver\BthEnum" ); + TRACE( "(%p, %s)\n", driver, debugstr_w( path->Buffer ) );
status = winebluetooth_init(); @@ -559,5 +639,7 @@ NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) driver->DriverExtension->AddDevice = driver_add_device; driver->DriverUnload = driver_unload; driver->MajorFunction[IRP_MJ_PNP] = bluetooth_pnp; + driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = dispatch_bluetooth; + IoCreateDriver( &driver_bthenum, bthenum_entry_point ); return STATUS_SUCCESS; } diff --git a/dlls/winebth.sys/winebth_priv.h b/dlls/winebth.sys/winebth_priv.h index e73586fead3..7ea7f03108d 100644 --- a/dlls/winebth.sys/winebth_priv.h +++ b/dlls/winebth.sys/winebth_priv.h @@ -21,6 +21,8 @@ #ifndef __WINE_WINEBTH_WINEBTH_H_ #define __WINE_WINEBTH_WINEBTH_H_
+#include "winebluetooth.h" + #ifdef __ASM_USE_FASTCALL_WRAPPER extern void * WINAPI wrap_fastcall_func1(void *func, const void *a); __ASM_STDCALL_FUNC(wrap_fastcall_func1, 8, @@ -32,11 +34,24 @@ __ASM_STDCALL_FUNC(wrap_fastcall_func1, 8, #else #define call_fastcall_func1(func,a) func(a) #endif +extern NTSTATUS WINAPI bthenum_device_added( + DEVICE_OBJECT *fdo_bus, struct winebluetooth_watcher_event_device_added event ); +extern NTSTATUS WINAPI bthenum_device_removed( DEVICE_OBJECT *fdo_bus, + const winebluetooth_device_t device_handle ); +extern NTSTATUS WINAPI bthenum_device_props_changed( + DEVICE_OBJECT *fdo_bus, struct winebluetooth_watcher_event_device_props_changed event ); +extern NTSTATUS WINAPI + bthenum_create_fdo_bus( DEVICE_OBJECT *pdo, DEVICE_OBJECT **fdo_bus ); +extern NTSTATUS WINAPI bthenum_entry_point( DRIVER_OBJECT *driver, UNICODE_STRING *path ); + struct string_buffer { WCHAR *string; size_t len; }; +extern void WINAPIV append_id( struct string_buffer *buffer, const WCHAR *format, ... ) + __WINE_PRINTF_ATTR( 2, 3 ); +
#define XX(i) case (i): return #i
@@ -87,7 +102,57 @@ static inline const char *debugstr_minor_function_code( UCHAR code ) return wine_dbg_sprintf( "(unknown %#x)", code ); } } + +static inline const char *debugstr_IoControlCode( ULONG code ) +{ + switch (code) + { + XX(IOCTL_BTH_DISCONNECT_DEVICE); + XX(IOCTL_BTH_GET_DEVICE_INFO); + XX(IOCTL_BTH_GET_LOCAL_INFO); + XX(IOCTL_BTH_GET_RADIO_INFO); + XX(IOCTL_BTH_SDP_ATTRIBUTE_SEARCH); + XX(IOCTL_BTH_SDP_CONNECT); + XX(IOCTL_BTH_SDP_DISCONNECT); + XX(IOCTL_BTH_SDP_REMOVE_RECORD); + XX(IOCTL_BTH_SDP_SERVICE_ATTRIBUTE_SEARCH); + XX(IOCTL_BTH_SDP_SERVICE_SEARCH); + XX(IOCTL_BTH_SDP_SUBMIT_RECORD); + XX(IOCTL_BTH_SDP_SUBMIT_RECORD_WITH_INFO); + default: + return wine_dbg_sprintf( "(unknown %#x)", code ); + } +} + #undef XX
+static inline const char *debugstr_radio_flags( UINT32 flags ) +{ + const UINT32 all_flags = LOCAL_RADIO_CONNECTABLE | LOCAL_RADIO_DISCOVERABLE; + static const UINT32 masks[] = {LOCAL_RADIO_CONNECTABLE, LOCAL_RADIO_DISCOVERABLE}; + static const char *masks_str[] = {"LOCAL_RADIO_CONNECTABLE", "LOCAL_RADIO_DISCOVERABLE"}; + char buf[80] = ""; + BOOL prev = FALSE; + SIZE_T i; + + for (i = 0; i < ARRAY_SIZE( masks ); i++) + { + if (flags & masks[i]) + { + if (prev) strcat( buf, "|" ); + strcat( buf, masks_str[i] ); + prev = TRUE; + } + } + + flags &= ~all_flags; + if (flags) + { + return wine_dbg_sprintf( "{%s (unknown %#x)}", flags ); + } + + return wine_dbg_sprintf("{%s}", buf); +} +
#endif /* __WINE_WINEBTH_WINEBTH_H_ */ diff --git a/include/Makefile.in b/include/Makefile.in index a62a24d327f..76494191f51 100644 --- a/include/Makefile.in +++ b/include/Makefile.in @@ -59,6 +59,7 @@ SOURCES = \ bitsmsg.h \ bluetoothapis.h \ bthdef.h \ + bthioctl.h \ bthsdpdef.h \ cderr.h \ cdosys.idl \ diff --git a/include/bthdef.h b/include/bthdef.h index 9b65cc8d48f..d431de929c9 100644 --- a/include/bthdef.h +++ b/include/bthdef.h @@ -23,6 +23,29 @@ extern "C" { #endif
+typedef ULONG BTH_COD; +#define BTH_MAX_NAME_SIZE (248) +#define MAX_UUIDS_IN_QUERY (12) + +#define BDIF_ADDRESS 0x00000001 +#define BDIF_COD 0x00000002 +#define BDIF_NAME 0x00000004 +#define BDIF_PAIRED 0x00000008 +#define BDIF_PERSONAL 0x00000010 +#define BDIF_CONNECTED 0x00000020 + +#define BDIF_SSP_SUPPORTED 0x00000100 +#define BDIF_SSP_PAIRED 0x00000200 +#define BDIF_SSP_MITM_PROTECTED 0x00000200 + +typedef struct _BTH_DEVICE_INFO +{ + ULONG flags; + BTH_ADDR address; + BTH_COD classOfDevice; + CHAR name[BTH_MAX_NAME_SIZE]; +} BTH_DEVICE_INFO, *PBTH_DEVICE_INFO; + DEFINE_GUID( GUID_BTHPORT_DEVICE_INTERFACE, 0x850302a, 0xb344, 0x4fda, 0x9b, 0xe9, 0x90, 0x57, 0x6b, 0x8d, 0x46, 0xf0 );
diff --git a/include/bthioctl.h b/include/bthioctl.h new file mode 100644 index 00000000000..773ea54f154 --- /dev/null +++ b/include/bthioctl.h @@ -0,0 +1,71 @@ +/* + * IOCTL definitions for interfacing with 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 __WINE_BTHIOCTL_H_ +#define __WINE_BTHIOCTL_H_ + +#include <winioctl.h> + +#define IOCTL_BTH_GET_LOCAL_INFO CTL_CODE(FILE_DEVICE_BLUETOOTH, 0x00, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_BTH_GET_RADIO_INFO CTL_CODE(FILE_DEVICE_BLUETOOTH, 0x01, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_BTH_GET_DEVICE_INFO CTL_CODE(FILE_DEVICE_BLUETOOTH, 0x02, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_BTH_DISCONNECT_DEVICE CTL_CODE(FILE_DEVICE_BLUETOOTH, 0x03, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_BTH_SDP_CONNECT CTL_CODE(FILE_DEVICE_BLUETOOTH, 0x80, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_BTH_SDP_DISCONNECT CTL_CODE(FILE_DEVICE_BLUETOOTH, 0x81, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_BTH_SDP_SERVICE_SEARCH CTL_CODE(FILE_DEVICE_BLUETOOTH, 0x82, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_BTH_SDP_ATTRIBUTE_SEARCH CTL_CODE(FILE_DEVICE_BLUETOOTH, 0x83, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_BTH_SDP_SERVICE_ATTRIBUTE_SEARCH CTL_CODE(FILE_DEVICE_BLUETOOTH, 0x84, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_BTH_SDP_SUBMIT_RECORD CTL_CODE(FILE_DEVICE_BLUETOOTH, 0x85, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_BTH_SDP_REMOVE_RECORD CTL_CODE(FILE_DEVICE_BLUETOOTH, 0x86, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_BTH_SDP_SUBMIT_RECORD_WITH_INFO CTL_CODE(FILE_DEVICE_BLUETOOTH, 0x87, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_INTERNAL_BTH_SUBMIT_BRB CTL_CODE(FILE_DEVICE_BLUETOOTH, 0x00, METHOD_NEITHER, FILE_ANY_ACCESS) +#define IOCTL_INTERNAL_BTHENUM_GET_ENUMINFO CTL_CODE(FILE_DEVICE_BLUETOOTH, 0x01, METHOD_NEITHER, FILE_ANY_ACCESS) +#define IOCTL_INTERNAL_BTHENUM_GET_DEVINFO CTL_CODE(FILE_DEVICE_BLUETOOTH, 0x02, METHOD_NEITHER, FILE_ANY_ACCESS) + +#define LOCAL_RADIO_DISCOVERABLE (0x0001) +#define LOCAL_RADIO_CONNECTABLE (0x0002) + +typedef struct _BTH_RADIO_INFO +{ + ULONGLONG lmpSupportedFeatures; + USHORT mfg; + USHORT lmpSubversion; + UCHAR lmpVersion; +} BTH_RADIO_INFO, *PBTH_RADIO_INFO; + +typedef struct _BTH_LOCAL_RADIO_INFO +{ + BTH_DEVICE_INFO localInfo; + ULONG flags; + USHORT hciRevision; + UCHAR hciVerison; + BTH_RADIO_INFO radioInfo; +} BTH_LOCAL_RADIO_INFO, *PBTH_LOCAL_RADIO_INFO; + +typedef struct _BTH_DEVICE_INFO_LIST +{ + ULONG numOfDevices; + BTH_DEVICE_INFO deviceList[1]; +} BTH_DEVICE_INFO_LIST, *PBTH_DEVICE_INFO_LIST; + +#endif /* __WINE_BTHIOCTL_H_ */ + diff --git a/include/ddk/bthguid.h b/include/ddk/bthguid.h index 067e9cac59a..ab6e6a7e112 100644 --- a/include/ddk/bthguid.h +++ b/include/ddk/bthguid.h @@ -27,6 +27,26 @@ DEFINE_GUID( GUID_BLUETOOTH_RADIO_INTERFACE, 0x92383b0e, 0xf90e, 0x4ac9, 0x8d, 0x44, 0x8c, 0x2d, 0x0d, 0x0e, 0xbd, 0xa2 );
+DEFINE_GUID( GUID_BTH_DEVICE_INTERFACE, 0x00f40965, 0xe89d, 0x4487, 0x98, 0x90, 0x87, 0xc3, 0xab, + 0xb2, 0x11, 0xf4 ); + +DEFINE_GUID( GUID_BLUETOOTHLE_DEVICE_INTERFACE, 0x781aee18, 0x7733, 0x4ce4, 0xad, 0xd0, 0x91, 0xf4, + 0x1c, 0x67, 0xb5, 0x92 ); + +#define DEFINE_BTH_DEVICE_DEVPROPKEY( d, i ) \ + DEFINE_DEVPROPKEY( DEVPKEY_Bluetooth_##d, 0x2bd67d8b, 0x8beb, 0x48d5, 0x87, 0xe0, 0x6c, 0xda, \ + 0x34, 0x28, 0x04, 0x0a, ( i ) ) + +DEFINE_BTH_DEVICE_DEVPROPKEY( DeviceAddress, 1 ); /* DEVPROP_TYPE_STRING */ +DEFINE_BTH_DEVICE_DEVPROPKEY( ServiceGUID, 2 ); /* DEVPROP_TYPE_GUID */ +DEFINE_BTH_DEVICE_DEVPROPKEY( DeviceFlags, 3 ); /* DEVPROP_TYPE_UINT32 */ +DEFINE_BTH_DEVICE_DEVPROPKEY( DeviceManufacturer, 4 ); /* DEVPROP_TYPE_UINT32 */ +DEFINE_BTH_DEVICE_DEVPROPKEY( DeviceVIDSource, 6 ); /* DEVPROP_TYPE_BYTE */ +DEFINE_BTH_DEVICE_DEVPROPKEY( DeviceVID, 7 ); /* DEVPROP_TYPE_UINT16 */ +DEFINE_BTH_DEVICE_DEVPROPKEY( DevicePID, 8 ); /* DEVPROP_TYPE_UINT16 */ +DEFINE_BTH_DEVICE_DEVPROPKEY( DeviceProductVersion, 9 ); /* DEVPROP_TYPE_UINT16 */ +DEFINE_BTH_DEVICE_DEVPROPKEY( ClassOfDevice, 10 ); /* DEVPROP_TYPE_UINT32 */ + #define DEFINE_BTH_RADIO_DEVPROPKEY( d, i ) \ DEFINE_DEVPROPKEY( DEVPKEY_BluetoothRadio_##d, 0xa92f26ca, 0xeda7, 0x4b1d, 0x9d, 0xb2, 0x27, \ 0xb6, 0x8a, 0xa5, 0xa2, 0xeb, ( i ) ) @@ -44,10 +64,17 @@ DEFINE_BTH_RADIO_DEVPROPKEY( LELocalSupportedFeatures, 22 ); /* DEVPROP_TY DEFINE_GUID( GUID_BLUETOOTH_WINE_AUTH_REQUEST, 0x1a188a9b, 0x2d33, 0x4269, 0x85, 0xea, 0xd5, 0x3d, 0x69, 0x18, 0xdc, 0xeb );
+#define DEFINE_WINEBTH_DEVICE_DEVPROPKEY( d, i ) \ + DEFINE_DEVPROPKEY( DEVPKEY_WineBluetooth_Device_##d, 0xa919542e, 0xf061, 0x4237, 0x8d, 0x61, \ + 0xe2, 0x3e, 0x73, 0x44, 0x58, 0x6f, (i) ) + #define DEFINE_WINEBTH_RADIO_DEVPROPKEY( d, i ) \ DEFINE_DEVPROPKEY( DEVPKEY_WineBluetooth_Radio_##d, 0x9ccd2c1a, 0x4b06, 0x4ecf, 0xb1, 0xd5, \ 0x35, 0x62, 0xd6, 0xb0, 0xda, 0xac, (i) )
+/* Name, as reported by the device. DEVPROP_TYPE_STRING */ +DEFINE_WINEBTH_DEVICE_DEVPROPKEY( Name, 1 ); + /* Authentication I/O capability as currently advertised by the radio. DEVPROP_TYPE_BYTE */ DEFINE_WINEBTH_RADIO_DEVPROPKEY( Capability, 1 ); #endif
From: Vibhav Pant vibhavp@gmail.com
--- dlls/winebth.sys/bthenum.c | 281 ++++++++++++++++++++++++++++++- dlls/winebth.sys/dbus.c | 67 +++++++- dlls/winebth.sys/unixlib.c | 18 ++ dlls/winebth.sys/unixlib.h | 16 ++ dlls/winebth.sys/winebluetooth.c | 20 +++ dlls/winebth.sys/winebluetooth.h | 13 +- dlls/winebth.sys/winebth.c | 2 +- dlls/winebth.sys/winebth_priv.h | 4 +- 8 files changed, 413 insertions(+), 8 deletions(-)
diff --git a/dlls/winebth.sys/bthenum.c b/dlls/winebth.sys/bthenum.c index 2de12b334ae..7e55cff1219 100644 --- a/dlls/winebth.sys/bthenum.c +++ b/dlls/winebth.sys/bthenum.c @@ -65,6 +65,7 @@ static DRIVER_OBJECT *driver_obj; enum bthenum_phys_device_kind { BTHENUM_PHYS_DEVICE_KIND_REMOTE_RADIO, + BTHENUM_PHYS_DEVICE_KIND_DEVICE_SERVICE, };
struct device_ext @@ -78,6 +79,7 @@ struct bthenum_func_local_radio_ext struct device_ext base; DEVICE_OBJECT *parent_radio_pdo; DEVICE_OBJECT *device_obj; + UINT16 localmfg;
RTL_CRITICAL_SECTION cs; /* Remote bluetooth radios associated with this radio (struct bthenum_phys_remote_radio) */ @@ -92,6 +94,18 @@ struct phys_device_ext struct bthenum_func_local_radio_ext *parent_fdo; };
+struct bthenum_phys_service_ext +{ + struct phys_device_ext base; + /* The remote device that this service instance belongs to */ + struct bthenum_phys_remote_radio_ext *remote_device; + DEVICE_OBJECT *device_obj; + struct list entry; + + GUID service_guid; + UNICODE_STRING service_link_name; +}; + /* Child PDO under a bluetooth radio FDO bus */ struct bthenum_phys_remote_radio_ext { @@ -104,6 +118,9 @@ struct bthenum_phys_remote_radio_ext struct winebluetooth_device_properties props;
UNICODE_STRING device_link_name; + + RTL_CRITICAL_SECTION cs; + struct list services; };
static struct bthenum_func_local_radio_ext *fdo_from_DEVICE_OBJECT( DEVICE_OBJECT *device ) @@ -144,10 +161,16 @@ NTSTATUS WINAPI bthenum_device_added( DEVICE_OBJECT *fdo_bus, child_device->known_props = event.known_props_mask; child_device->device_obj = child_pdo;
+ list_init( &child_device->services ); + RtlEnterCriticalSection( &fdo->cs ); list_add_tail(&fdo->devices, &child_device->entry); RtlLeaveCriticalSection( &fdo->cs );
+ RtlInitializeCriticalSectionEx( &child_device->cs, 0, + RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO ); + child_device->cs.DebugInfo->Spare[0] = (DWORD_PTR)( __FILE__ ": bthenum_phys_remote_radio.cs" ); + IoInvalidateDeviceRelations( fdo_bus, BusRelations );
return STATUS_SUCCESS; @@ -167,8 +190,21 @@ NTSTATUS WINAPI bthenum_device_removed( DEVICE_OBJECT *fdo_bus, if (winebluetooth_device_equal( device_handle, remote_device->remote_radio_handle ) && !remote_device->base.base.removed) { + struct bthenum_phys_service_ext *service; remote_device->base.base.removed = TRUE; list_remove( &remote_device->entry ); + + RtlEnterCriticalSection( &remote_device->cs ); + LIST_FOR_EACH_ENTRY( service, &remote_device->services, struct bthenum_phys_service_ext, + entry ) + { + if (!service->base.base.removed) + { + service->base.base.removed = TRUE; + list_remove( &service->entry ); + } + } + RtlLeaveCriticalSection( &remote_device->cs ); break; } } @@ -220,7 +256,7 @@ bthenum_device_props_changed( DEVICE_OBJECT *fdo_bus, }
/* Called once for every Bluetooth Radio discovered. */ -NTSTATUS WINAPI bthenum_create_fdo_bus( DEVICE_OBJECT *pdo, DEVICE_OBJECT **fdo_bus ) +NTSTATUS bthenum_create_fdo_bus( DEVICE_OBJECT *pdo, DEVICE_OBJECT **fdo_bus, UINT16 localmfg ) { struct bthenum_func_local_radio_ext *fdo; NTSTATUS status; @@ -240,6 +276,7 @@ NTSTATUS WINAPI bthenum_create_fdo_bus( DEVICE_OBJECT *pdo, DEVICE_OBJECT **fdo_ fdo->base.removed = FALSE; fdo->parent_radio_pdo = pdo; fdo->device_obj = *fdo_bus; + fdo->localmfg = localmfg; list_init( &fdo->devices );
RtlInitializeCriticalSectionEx( &fdo->cs, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO ); @@ -446,7 +483,7 @@ static NTSTATUS WINAPI fdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) struct bthenum_phys_remote_radio_ext *remote; DEVICE_RELATIONS *devices; SIZE_T i = 0; - SIZE_T objects_count; + SIZE_T objects_count = 0;
if (stack->Parameters.QueryDeviceRelations.Type != BusRelations) { @@ -454,7 +491,12 @@ static NTSTATUS WINAPI fdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) }
RtlEnterCriticalSection( &device->cs ); - objects_count = list_count( &device->devices ); + LIST_FOR_EACH_ENTRY( remote, &device->devices, struct bthenum_phys_remote_radio_ext, entry ) + { + objects_count++; + objects_count += list_count(&remote->services); + } + devices = ExAllocatePool( PagedPool, offsetof( DEVICE_RELATIONS, Objects[objects_count] ) ); if (devices == NULL) @@ -466,8 +508,16 @@ static NTSTATUS WINAPI fdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp )
LIST_FOR_EACH_ENTRY( remote, &device->devices, struct bthenum_phys_remote_radio_ext, entry ) { + struct bthenum_phys_service_ext *service; devices->Objects[i++] = remote->device_obj; call_fastcall_func1( ObfReferenceObject, remote->device_obj ); + + LIST_FOR_EACH_ENTRY( service, &remote->services, struct bthenum_phys_service_ext, + entry ) + { + devices->Objects[i++] = service->device_obj; + call_fastcall_func1(ObfReferenceObject, service->device_obj); + } } RtlLeaveCriticalSection( &device->cs ); assert( objects_count == i ); @@ -554,6 +604,108 @@ static NTSTATUS pdo_device_query_id( const struct bthenum_phys_remote_radio_ext return irp->IoStatus.Status; }
+struct guid_entry +{ + GUID guid; + struct rb_entry entry; +}; + +static int guid_entry_compare( const void *key, const struct rb_entry *entry ) +{ + return memcmp( key, RB_ENTRY_VALUE( entry, struct guid_entry, entry ), sizeof( GUID ) ); +} + +#define GUID_FORMAT "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}" +#define GUID_FORMAT_ARGS(guid) \ + (guid).Data1, (guid).Data2, (guid).Data3, (guid).Data4[0], (guid).Data4[1], \ + (guid).Data4[2], (guid).Data4[3], (guid).Data4[4], (guid).Data4[5], \ + (guid).Data4[6], (guid).Data4[7] + +static NTSTATUS WINAPI remote_device_refresh_services( struct bthenum_phys_remote_radio_ext *device, + GUID *arr, SIZE_T len ) +{ + struct rb_tree guid_tree; + struct guid_entry *new_guid; + struct bthenum_phys_service_ext *service; + struct bthenum_func_local_radio_ext *fdo_bus_ext = device->base.parent_fdo; + struct guid_entry *guid_entries; + NTSTATUS ret = STATUS_SUCCESS; + BOOL invalidate_bus_relations = FALSE; + SIZE_T i; + + TRACE("(%p, %p, %ld)\n", device, arr, len); + + guid_entries = calloc( len, sizeof( struct guid_entry ) ); + if (!guid_entries) + { + return STATUS_NO_MEMORY; + } + + rb_init( &guid_tree, guid_entry_compare ); + for ( i = 0; i < len; i++ ) + { + guid_entries[i].guid = arr[i]; + rb_put(&guid_tree, &arr[i], &guid_entries[i].entry); + } + + RtlEnterCriticalSection(&fdo_bus_ext->cs); + /* Go through all existing services for this device, and remove them the device's service list + * if they are not in guid_tree. Otherwise, remove them from guid_tree. */ + LIST_FOR_EACH_ENTRY( service, &device->services, struct bthenum_phys_service_ext, entry ) + { + struct rb_entry *guid_entry = rb_get( &guid_tree, &service->service_guid ); + + if (guid_entry != NULL) + { + /* The service already exists under this device, remove it from guid_tree. */ + rb_remove( &guid_tree, guid_entry ); + } + else + { + /* The service is no longer offered by the device, mark it as removed. */ + invalidate_bus_relations = TRUE; + service->base.base.removed = TRUE; + list_remove( &service->entry ); + } + } + /* Any entries remaining in guid_tree now need to be created under this device. */ + RB_FOR_EACH_ENTRY( new_guid, &guid_tree, struct guid_entry, entry ) + { + struct bthenum_phys_service_ext *service_device; + DEVICE_OBJECT *service_device_obj; + NTSTATUS status; + + if (IsEqualGUID(&new_guid->guid, &GUID_NULL)) continue; + + status = IoCreateDevice( fdo_bus_ext->device_obj->DriverObject, sizeof( *service_device ), + NULL, FILE_DEVICE_BLUETOOTH, 0, FALSE, &service_device_obj ); + if ( status != STATUS_SUCCESS ) + { + ERR( "failed to create sevice device: %#lx\n", status ); + ret = status; + goto done; + } + service_device = service_device_obj->DeviceExtension; + service_device->base.base.is_fdo = FALSE; + service_device->base.base.removed = FALSE; + service_device->base.kind = BTHENUM_PHYS_DEVICE_KIND_DEVICE_SERVICE; + service_device->base.parent_fdo = fdo_bus_ext; + + service_device->remote_device = device; + list_add_tail( &device->services, &service_device->entry ); + service_device->device_obj = service_device_obj; + service_device->service_guid = new_guid->guid; + memset( &service_device->service_link_name, 0, + sizeof( service_device->service_link_name ) ); + } + done: + RtlLeaveCriticalSection(&fdo_bus_ext->cs); + free(guid_entries); + if (invalidate_bus_relations) + IoInvalidateDeviceRelations( fdo_bus_ext->device_obj, BusRelations ); + return ret; +} + static NTSTATUS WINAPI pdo_device_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) { struct bthenum_phys_remote_radio_ext *device = @@ -568,6 +720,7 @@ static NTSTATUS WINAPI pdo_device_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) case IRP_MN_START_DEVICE: { GUID g = GUID_NULL; + GUID *guids = NULL;
if ( IoRegisterDeviceInterface( device->device_obj, &GUID_BTH_DEVICE_INTERFACE, NULL, &device->device_link_name ) == STATUS_SUCCESS ) @@ -575,6 +728,19 @@ static NTSTATUS WINAPI pdo_device_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) IoSetDevicePropertyData( device_obj, &DEVPKEY_Bluetooth_ServiceGUID, LOCALE_NEUTRAL, 0, DEVPROP_TYPE_GUID, sizeof( GUID_NULL ), &g ); device_set_properties( device_obj, NULL, device->known_props, device->props ); + if (device->props.uuids_len != 0) + { + guids = calloc( device->props.uuids_len, sizeof( GUID ) ); + if (!guids) + { + status = STATUS_NO_MEMORY; + break; + } + winebluetooth_guid_array_copy( device->props.guids, device->props.uuids_len, guids ); + winebluetooth_guid_array_free( device->props.guids ); + } + remote_device_refresh_services( device, guids, device->props.uuids_len ); + if (guids) free( guids ); break; } case IRP_MN_SURPRISE_REMOVAL: @@ -623,6 +789,113 @@ static NTSTATUS WINAPI pdo_device_pnp( DEVICE_OBJECT *device_obj, IRP *irp )
#define LOCALMFG_FORMAT "LOCALMFG&%04x"
+static NTSTATUS pdo_service_query_id( const struct bthenum_phys_service_ext *service, IRP *irp, + BUS_QUERY_ID_TYPE type, struct string_buffer *buf ) +{ + struct winebluetooth_device_pnpid pnpid = service->remote_device->props.pnpid; + + TRACE( "service=%p irp=%p type=%s buf=%p\n", service, irp, debugstr_BUS_QUERY_ID_TYPE( type ), buf ); + + switch(type) + { + case BusQueryInstanceID: + { + BYTE *addr = service->remote_device->props.address.rgBytes; + append_id( buf, L"%p&%02X%02X%02X%02X%02X%02X", + service->remote_device->remote_radio_handle, addr[0], addr[1], addr[2], + addr[3], addr[4], addr[5] ); + break; + } + case BusQueryDeviceID: + append_id( buf, L"BTHENUM\" GUID_FORMAT "_" PNPID_FORMAT, + GUID_FORMAT_ARGS( service->service_guid ), PNPID_FORMAT_ARGS( pnpid ) ); + break; + case BusQueryHardwareIDs: + { + append_id( buf, L"BTHENUM\" GUID_FORMAT "_" PNPID_FORMAT, + GUID_FORMAT_ARGS( service->service_guid ), PNPID_FORMAT_ARGS( pnpid ) ); + append_id( buf, L"BTHENUM\" GUID_FORMAT "_" LOCALMFG_FORMAT, + GUID_FORMAT_ARGS( service->service_guid ), service->base.parent_fdo->localmfg ); + append_id( buf, L"" ); + break; + } + case BusQueryCompatibleIDs: + { + append_id( buf, L"BTHENUM\" GUID_FORMAT, GUID_FORMAT_ARGS( service->service_guid ) ); + append_id( buf, L"BTHENUM\" GUID_FORMAT "_GENERIC", + GUID_FORMAT_ARGS( service->service_guid ) ); + append_id( buf, L"" ); + break; + } + default: + FIXME( "Unhandled ID query type %s\n", debugstr_BUS_QUERY_ID_TYPE( type ) ); + return irp->IoStatus.Status; + } + + if ( buf->string == NULL ) irp->IoStatus.Status = STATUS_NO_MEMORY; + else + { + irp->IoStatus.Status = STATUS_SUCCESS; + irp->IoStatus.Information = (ULONG_PTR)buf->string; + } + + return irp->IoStatus.Status; +} + +static NTSTATUS WINAPI pdo_service_pnp( DEVICE_OBJECT *device, IRP *irp ) +{ + struct bthenum_phys_service_ext *service = (struct bthenum_phys_service_ext *)device->DeviceExtension; + IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp ); + UCHAR code = stack->MinorFunction; + NTSTATUS status = irp->IoStatus.Status; + + TRACE( "device %p, irp %p, code %s\n", device, irp, debugstr_minor_function_code( code ) ); + switch (code) + { + case IRP_MN_START_DEVICE: + status = IoRegisterDeviceInterface( device, &service->service_guid, NULL, + &service->service_link_name ); + if (status == STATUS_SUCCESS) + status = IoSetDeviceInterfaceState( &service->service_link_name, TRUE ); + break; + case IRP_MN_SURPRISE_REMOVAL: + if (!service->base.base.removed) + { + service->base.base.removed = TRUE; + list_remove( &service->entry ); + } + status = STATUS_SUCCESS; + break; + case IRP_MN_REMOVE_DEVICE: + assert( service->base.base.removed ); + irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest( irp, IO_NO_INCREMENT ); + IoDeleteDevice( device ); + return STATUS_SUCCESS; + case IRP_MN_QUERY_ID: + { + BUS_QUERY_ID_TYPE type = stack->Parameters.QueryId.IdType; + struct string_buffer buf = { 0 }; + + status = pdo_service_query_id( service, irp, type, &buf ); + break; + } + case IRP_MN_QUERY_CAPABILITIES: + { + DEVICE_CAPABILITIES *caps = stack->Parameters.DeviceCapabilities.Capabilities; + caps->Removable = TRUE; + caps->SurpriseRemovalOK = TRUE; + caps->RawDeviceOK = TRUE; + status = STATUS_SUCCESS; + break; + } + } + + irp->IoStatus.Status = status; + IoCompleteRequest(irp, IO_NO_INCREMENT); + return status; +} + static NTSTATUS WINAPI pdo_pnp( DEVICE_OBJECT *device, IRP *irp ) { struct phys_device_ext *base = (struct phys_device_ext *)device->DeviceExtension; @@ -630,6 +903,8 @@ static NTSTATUS WINAPI pdo_pnp( DEVICE_OBJECT *device, IRP *irp ) { case BTHENUM_PHYS_DEVICE_KIND_REMOTE_RADIO: return pdo_device_pnp( device, irp ); + case BTHENUM_PHYS_DEVICE_KIND_DEVICE_SERVICE: + return pdo_service_pnp( device, irp ); DEFAULT_UNREACHABLE; } } diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index 1809cfc6cf5..c7b6b723050 100644 --- a/dlls/winebth.sys/dbus.c +++ b/dlls/winebth.sys/dbus.c @@ -119,6 +119,41 @@ static inline int starts_with( const char *str, const char *prefix ) return !strncmp( str, prefix, strlen( prefix ) ); }
+static BOOL parse_uuid( GUID *guid, const char *str ) +{ + /* standard uuid format */ + if (strlen(str) == 36 && str[8] == '-' && str[13] == '-' && str[18] == '-' && str[23] == '-') + { + int i; + unsigned char *out = guid->Data4; + + if (sscanf( str, "%x-%hx-%hx-", (int *)&guid->Data1, &guid->Data2, &guid->Data3 ) != 3) return FALSE; + for (i = 19; i < 36; i++) + { + unsigned char val; + + if (i == 23) continue; + + if (str[i] >= '0' && str[i] <= '9') val = str[i] - '0'; + else if (str[i] >= 'a' && str[i] <= 'f') val = str[i] - 'a' + 10; + else if (str[i] >= 'A' && str[i] <= 'F') val = str[i] - 'A' + 10; + else return FALSE; + val <<= 4; + i++; + + if (str[i] >= '0' && str[i] <= '9') val += str[i] - '0'; + else if (str[i] >= 'a' && str[i] <= 'f') val += str[i] - 'a' + 10; + else if (str[i] >= 'A' && str[i] <= 'F') val += str[i] - 'A' + 10; + else return FALSE; + *out++ = val; + } + return TRUE; + } + + + return FALSE; +} + static NTSTATUS bluez_dbus_error_to_ntstatus( const DBusError *error ) {
@@ -476,6 +511,34 @@ static void bluez_device_prop_from_dict_entry( const char *prop_name, DBusMessag &props->pnpid.product_version ); *props_mask |= WINEBLUETOOTH_DEVICE_PROPERTY_PNPID; } + else if ( wanted_props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_UUIDS && + !strcmp( prop_name, "UUIDs" ) && + p_dbus_message_iter_get_arg_type( variant ) == DBUS_TYPE_ARRAY ) + { + DBusMessageIter array_iter; + props->uuids_len = p_dbus_message_iter_get_element_count( variant ); + *props_mask |= WINEBLUETOOTH_DEVICE_PROPERTY_UUIDS; + + if (props->uuids_len != 0) + { + SIZE_T i = 0; + props->guids = calloc( props->uuids_len, sizeof( GUID ) ); + if (!props->guids) + { + props->uuids_len = 0; + return; + } + p_dbus_message_iter_recurse( variant, &array_iter ); + while(p_dbus_message_iter_has_next( &array_iter )) + { + const char *uuid_str = NULL; + + p_dbus_message_iter_get_basic( &array_iter, &uuid_str ); + parse_uuid( &props->guids[i++], uuid_str ); + p_dbus_message_iter_next( &array_iter ); + } + } + } }
NTSTATUS @@ -501,7 +564,8 @@ bluez_device_props_get_all_reply_to_params(
bluez_device_prop_from_dict_entry( prop_name, &variant, params->get_device_reported_name, ¶ms->props, &prop, - WINEBLUETOOTH_DEVICE_ALL_PROPERTIES ); + WINEBLUETOOTH_DEVICE_ALL_PROPERTIES + & ~WINEBLUETOOTH_DEVICE_PROPERTY_UUIDS ); params->valid_props |= prop; }
@@ -985,6 +1049,7 @@ static DBusHandlerResult bluez_filter( DBusConnection *conn, DBusMessage *msg, v { "LegacyPairing", WINEBLUETOOTH_DEVICE_PROPERTY_LEGACY_PAIRING_HINT }, { "ServicesResolved", WINEBLUETOOTH_DEVICE_PROPERTY_SERVICES_RESOLVED }, { "Modalias", WINEBLUETOOTH_DEVICE_PROPERTY_PNPID }, + { "UUIDs", WINEBLUETOOTH_DEVICE_PROPERTY_UUIDS } }; const char *object_path = p_dbus_message_get_path( msg );
diff --git a/dlls/winebth.sys/unixlib.c b/dlls/winebth.sys/unixlib.c index 9deaa848975..79fdb101648 100644 --- a/dlls/winebth.sys/unixlib.c +++ b/dlls/winebth.sys/unixlib.c @@ -179,6 +179,21 @@ static NTSTATUS bluetooth_event_loop_once( void *args ) return bluez_dbus_loop( dbus_connection, params->unix_watcher_ctx, ¶ms->result ); }
+static NTSTATUS bluetooth_guid_array_copy( void *args ) +{ + struct bluetooth_guid_array_copy_params *params = args; + + memcpy( params->dst, (GUID *)params->src, params->src_len * sizeof( GUID ) ); + return STATUS_SUCCESS; +} + +static NTSTATUS bluetooth_guid_array_free( void *args ) +{ + struct bluetooth_guid_array_free_params *params = args; + free( (GUID *)params->arr ); + return STATUS_SUCCESS; +} + const unixlib_entry_t __wine_unix_call_funcs[] = { bluetooth_init, bluetooth_shutdown, @@ -192,6 +207,9 @@ const unixlib_entry_t __wine_unix_call_funcs[] = { bluetooth_watcher_close,
bluetooth_event_loop_once, + + bluetooth_guid_array_copy, + bluetooth_guid_array_free, };
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 e3e60411d3b..15fd76c1366 100644 --- a/dlls/winebth.sys/unixlib.h +++ b/dlls/winebth.sys/unixlib.h @@ -103,6 +103,19 @@ struct bluetooth_event_loop_once_params struct winebluetooth_event_loop_result result; };
+struct bluetooth_guid_array_copy_params +{ + UINT_PTR src; + SIZE_T src_len; + + GUID *dst; +}; + +struct bluetooth_guid_array_free_params +{ + UINT_PTR arr; +}; + enum bluetoothapis_funcs { unix_bluetooth_init, @@ -118,6 +131,9 @@ enum bluetoothapis_funcs
unix_bluetooth_event_loop_once,
+ unix_bluetooth_guid_array_copy, + unix_bluetooth_guid_array_free, + unix_funcs_count };
diff --git a/dlls/winebth.sys/winebluetooth.c b/dlls/winebth.sys/winebluetooth.c index fc4cad3115b..0cc499d7acb 100644 --- a/dlls/winebth.sys/winebluetooth.c +++ b/dlls/winebth.sys/winebluetooth.c @@ -73,6 +73,26 @@ void winebluetooth_device_free( winebluetooth_device_t device ) UNIX_BLUETOOTH_CALL( bluetooth_device_free, ¶ms ); }
+NTSTATUS winebluetooth_guid_array_copy( UINT_PTR src, SIZE_T len, GUID *dst ) +{ + struct bluetooth_guid_array_copy_params params = {0}; + + TRACE( "(%p, %ld, %p)\n", src, len, dst ); + params.src = src; + params.src_len = len; + params.dst = dst; + return UNIX_BLUETOOTH_CALL( bluetooth_guid_array_copy, ¶ms ); +} + +NTSTATUS winebluetooth_guid_array_free( UINT_PTR src ) +{ + struct bluetooth_guid_array_free_params params = {0}; + + TRACE( "(%p)\n", src ); + params.arr = src; + return UNIX_BLUETOOTH_CALL( bluetooth_guid_array_free, ¶ms ); +} + NTSTATUS winebluetooth_watcher_init( winebluetooth_watcher_t *watcher ) { struct bluetooth_watcher_init_params args = { 0 }; diff --git a/dlls/winebth.sys/winebluetooth.h b/dlls/winebth.sys/winebluetooth.h index 97d978b9e0c..ff471855edb 100644 --- a/dlls/winebth.sys/winebluetooth.h +++ b/dlls/winebth.sys/winebluetooth.h @@ -108,6 +108,12 @@ struct winebluetooth_device_properties BOOL legacy_pairing_hint; BOOL services_resolved; struct winebluetooth_device_pnpid pnpid; + SIZE_T uuids_len; +#ifdef WINE_UNIX_LIB + GUID *guids; +#else + UINT_PTR guids; +#endif };
enum winebluetooth_discovery_transport @@ -142,6 +148,9 @@ static inline BOOL winebluetooth_device_equal( winebluetooth_device_t d1, return d1.handle == d2.handle; }
+NTSTATUS winebluetooth_guid_array_copy( UINT_PTR src, SIZE_T uuids, GUID *arr ); +NTSTATUS winebluetooth_guid_array_free( UINT_PTR src ); + typedef struct { #ifdef WINE_UNIX_LIB @@ -172,6 +181,7 @@ enum winebluetooth_watcher_event_type #define WINEBLUETOOTH_DEVICE_PROPERTY_LEGACY_PAIRING_HINT (1 << 9) #define WINEBLUETOOTH_DEVICE_PROPERTY_SERVICES_RESOLVED (1 << 10) #define WINEBLUETOOTH_DEVICE_PROPERTY_PNPID (1 << 11) +#define WINEBLUETOOTH_DEVICE_PROPERTY_UUIDS (1 << 12)
#define WINEBLUETOOTH_DEVICE_ALL_PROPERTIES \ ( WINEBLUETOOTH_DEVICE_PROPERTY_ADDRESS | WINEBLUETOOTH_DEVICE_PROPERTY_ADDRESS_TYPE | \ @@ -179,7 +189,8 @@ enum winebluetooth_watcher_event_type WINEBLUETOOTH_DEVICE_PROPERTY_TRUSTED | WINEBLUETOOTH_DEVICE_PROPERTY_PAIRED | \ WINEBLUETOOTH_DEVICE_PROPERTY_BONDED | WINEBLUETOOTH_DEVICE_PROPERTY_NAME | \ WINEBLUETOOTH_DEVICE_PROPERTY_LEGACY_PAIRING_HINT | \ - WINEBLUETOOTH_DEVICE_PROPERTY_SERVICES_RESOLVED | WINEBLUETOOTH_DEVICE_PROPERTY_PNPID ) + WINEBLUETOOTH_DEVICE_PROPERTY_SERVICES_RESOLVED | WINEBLUETOOTH_DEVICE_PROPERTY_PNPID | \ + WINEBLUETOOTH_DEVICE_PROPERTY_UUIDS )
enum winebluetooth_device_connection_type { diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index 637d4933752..144bf546ba7 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -211,7 +211,7 @@ static void add_bluetooth_radio( struct winebluetooth_watcher_event_radio_added list_add_tail( &device_list, &device->entry ); LeaveCriticalSection( &device_list_cs );
- bthenum_create_fdo_bus( device_obj, &device->device_fdo_bus ); + bthenum_create_fdo_bus( device_obj, &device->device_fdo_bus, event.props.manufacturer ); IoInvalidateDeviceRelations( bus_pdo, BusRelations ); }
diff --git a/dlls/winebth.sys/winebth_priv.h b/dlls/winebth.sys/winebth_priv.h index 7ea7f03108d..32d0d2ad95b 100644 --- a/dlls/winebth.sys/winebth_priv.h +++ b/dlls/winebth.sys/winebth_priv.h @@ -40,8 +40,8 @@ extern NTSTATUS WINAPI bthenum_device_removed( DEVICE_OBJECT *fdo_bus, const winebluetooth_device_t device_handle ); extern NTSTATUS WINAPI bthenum_device_props_changed( DEVICE_OBJECT *fdo_bus, struct winebluetooth_watcher_event_device_props_changed event ); -extern NTSTATUS WINAPI - bthenum_create_fdo_bus( DEVICE_OBJECT *pdo, DEVICE_OBJECT **fdo_bus ); +extern NTSTATUS WINAPI bthenum_create_fdo_bus( DEVICE_OBJECT *pdo, DEVICE_OBJECT **fdo_bus, + UINT16 localmfg ); extern NTSTATUS WINAPI bthenum_entry_point( DRIVER_OBJECT *driver, UNICODE_STRING *path );
struct string_buffer
From: Vibhav Pant vibhavp@gmail.com
--- dlls/winebth.sys/dbus.c | 190 +++++++++++++++++++++++++++---- dlls/winebth.sys/winebluetooth.h | 12 ++ dlls/winebth.sys/winebth.c | 124 ++++++++++++++++---- include/ddk/bthguid.h | 10 ++ 4 files changed, 294 insertions(+), 42 deletions(-)
diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index c7b6b723050..de083925513 100644 --- a/dlls/winebth.sys/dbus.c +++ b/dlls/winebth.sys/dbus.c @@ -655,6 +655,29 @@ static NTSTATUS bluez_device_get_props_by_path_async( return STATUS_SUCCESS; }
+static NTSTATUS bluez_adapter_get_props_async( void *connection, const char *radio_object_path, + DBusPendingCall **call ) +{ + DBusMessage *request; + static const char *adapter_iface = BLUEZ_INTERFACE_ADAPTER; + dbus_bool_t success; + + request = p_dbus_message_new_method_call( BLUEZ_DEST, radio_object_path, + DBUS_INTERFACE_PROPERTIES, "GetAll" ); + if (!request) return STATUS_NO_MEMORY; + + p_dbus_message_append_args( request, DBUS_TYPE_STRING, &adapter_iface, DBUS_TYPE_INVALID ); + + success = p_dbus_connection_send_with_reply( connection, request, call, bluez_timeout ); + p_dbus_message_unref( request ); + if (!success) + return STATUS_NO_MEMORY; + if (!*call) + return STATUS_INTERNAL_ERROR; + + return STATUS_SUCCESS; +} + struct bluez_pair_complete_params { void *user_data; @@ -778,6 +801,39 @@ static BOOL bluez_event_list_queue_new_event( struct list *event_list, return bluez_event_list_queue_new_event_with_call( event_list, event_type, event, NULL, NULL ); }
+static void bluez_filter_radio_props_changed_callback( DBusPendingCall *call, void *user_data ) +{ + union winebluetooth_watcher_event_data *event = user_data; + struct winebluetooth_watcher_event_radio_props_changed *changed = &event->radio_props_changed; + const struct unix_name *radio = event->radio_props_changed.radio.handle; + DBusMessage *reply = p_dbus_pending_call_steal_reply( call ); + DBusMessageIter dict, prop_iter, variant; + const char *prop_name; + DBusError error; + + TRACE( "call %p, radio %s\n", call, debugstr_a( changed->radio.handle ) ); + + p_dbus_error_init( &error ); + if (p_dbus_set_error_from_message( &error, reply )) + { + ERR( "Failed to get adapter properties for %s: %s: %s\n", debugstr_a( radio->str ), + debugstr_a( error.name ), debugstr_a( error.message ) ); + p_dbus_error_free( &error ); + p_dbus_message_unref( reply ); + return; + } + p_dbus_error_free( &error ); + + p_dbus_message_iter_init( reply, &dict ); + p_dbus_message_iter_recurse( &dict, &prop_iter ); + while((prop_name = bluez_next_dict_entry( &prop_iter, &variant ))) + { + bluez_radio_prop_from_dict_entry( prop_name, &variant, &changed->props, + &changed->changed_props_mask, changed->invalid_props_mask ); + } + changed->invalid_props_mask &= ~changed->changed_props_mask; + p_dbus_message_unref( reply ); +} static void bluez_filter_device_props_changed_callback( DBusPendingCall *call, void *user_data ) { union winebluetooth_watcher_event_data *event = user_data; @@ -813,6 +869,37 @@ static void bluez_filter_device_props_changed_callback( DBusPendingCall *call, v p_dbus_message_unref( reply ); }
+struct bluez_object_property_masks +{ + const char *prop_name; + UINT16 mask; +}; + +static UINT16 bluez_dbus_get_invalidated_properties_from_iter( + DBusMessageIter *invalid_prop_iter, const struct bluez_object_property_masks *prop_masks, + SIZE_T len ) +{ + UINT16 mask = 0; + + while (p_dbus_message_iter_has_next( invalid_prop_iter )) + { + const char *prop_name; + SIZE_T i; + + assert( p_dbus_message_iter_get_arg_type( invalid_prop_iter ) == DBUS_TYPE_STRING ); + p_dbus_message_iter_get_basic( invalid_prop_iter, &prop_name ); + for (i = 0; i < len; i++) + { + if (strcmp( prop_masks[i].prop_name, prop_name ) == 0) + mask |= prop_masks[i].mask; + } + + p_dbus_message_iter_next( invalid_prop_iter ); + } + + return mask; +} + static DBusHandlerResult bluez_filter( DBusConnection *conn, DBusMessage *msg, void *user_data ) { struct list *event_list; @@ -1026,18 +1113,93 @@ static DBusHandlerResult bluez_filter( DBusConnection *conn, DBusMessage *msg, v p_dbus_message_iter_get_basic( &iter, &iface ); assert( iface != NULL );
- if (strcmp( iface, BLUEZ_INTERFACE_DEVICE ) == 0) + if (strcmp( iface, BLUEZ_INTERFACE_ADAPTER ) == 0) + { + struct winebluetooth_watcher_event_radio_props_changed props_changed = {0}; + struct unix_name *radio; + DBusMessageIter changed_props_iter, invalid_props_iter, variant; + const char *prop_name; + const static struct bluez_object_property_masks radio_prop_masks[] = { + { "Name", WINEBLUTOOTH_RADIO_PROPERTY_NAME }, + { "Address", WINEBLUETOOTH_RADIO_PROPERTY_ADDRESS }, + { "Discoverable", WINEBLUETOOTH_RADIO_PROPERTY_DISCOVERABLE }, + { "Powered", WINEBLUETOOTH_RADIO_PROPERTY_CONNECTABLE }, + { "Class", WINEBLUETOOTH_RADIO_PROPERTY_CLASS }, + { "Manufacturer", WINEBLUETOOTH_RADIO_PROPERTY_MANUFACTURER }, + { "Version", WINEBLUETOOTH_RADIO_PROPERTY_VERSION }, + }; + const char *object_path = p_dbus_message_get_path( msg ); + + radio = unix_name_get_or_create( object_path ); + if (!radio) + { + ERR( "failed to allocate memory for adapter path %s\n", debugstr_a( object_path ) ); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + p_dbus_message_iter_next( &iter ); + p_dbus_message_iter_recurse( &iter, &changed_props_iter ); + while ((prop_name = bluez_next_dict_entry( &changed_props_iter, &variant ))) + { + bluez_radio_prop_from_dict_entry( prop_name, &variant, &props_changed.props, + &props_changed.changed_props_mask, + WINEBLUETOOTH_RADIO_ALL_PROPERTIES ); + } + + p_dbus_message_iter_next( &iter ); + p_dbus_message_iter_recurse( &iter, &invalid_props_iter ); + props_changed.invalid_props_mask = bluez_dbus_get_invalidated_properties_from_iter( + &invalid_props_iter, radio_prop_masks, ARRAY_SIZE( radio_prop_masks ) ); + + TRACE( "Properties changed for radio %s, changed %#x, invalid %#x\n", + debugstr_a( radio->str ), props_changed.changed_props_mask, + props_changed.invalid_props_mask ); + if (props_changed.invalid_props_mask != 0) + { + DBusPendingCall *pending_call = NULL; + union winebluetooth_watcher_event_data event = { .radio_props_changed = props_changed }; + NTSTATUS status = bluez_adapter_get_props_async( conn, radio->str, &pending_call ); + + if (status != STATUS_SUCCESS) + { + ERR( "Failed to create async call to get adapter properties: %#x\n", + (int)status ); + unix_name_free( radio ); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + if (!bluez_event_list_queue_new_event_with_call( event_list, + BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_PROPERTIES_CHANGED, + event, pending_call, + bluez_filter_radio_props_changed_callback )) + { + unix_name_free( radio ); + p_dbus_pending_call_cancel( pending_call ); + p_dbus_pending_call_unref( pending_call ); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + } + else + { + union winebluetooth_watcher_event_data event = { .radio_props_changed = + props_changed }; + if (!bluez_event_list_queue_new_event( event_list, + BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_PROPERTIES_CHANGED, + event )) + { + unix_name_free( radio ); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + } + } + else if (strcmp( iface, BLUEZ_INTERFACE_DEVICE ) == 0) { struct winebluetooth_watcher_event_device_props_changed props_changed = {0}; struct unix_name *device, *radio; DBusPendingCall *pending_call = NULL; DBusMessageIter changed_props_iter, invalid_props_iter, variant; const char *prop_name; - const static struct - { - const char *prop_str; - winebluetooth_device_props_mask_t prop_mask; - } device_props_mask_map[] = { + const static struct bluez_object_property_masks device_prop_masks[] = { { "Address", WINEBLUETOOTH_DEVICE_PROPERTY_ADDRESS }, { "AddressType", WINEBLUETOOTH_DEVICE_PROPERTY_ADDRESS_TYPE }, { "Class", WINEBLUETOOTH_DEVICE_PROPERTY_CLASS }, @@ -1077,20 +1239,8 @@ static DBusHandlerResult bluez_filter( DBusConnection *conn, DBusMessage *msg, v } p_dbus_message_iter_next( &iter ); p_dbus_message_iter_recurse( &iter, &invalid_props_iter ); - while (p_dbus_message_iter_has_next( &invalid_props_iter )) - { - const char *invalid_prop_name = NULL; - SIZE_T i; - - p_dbus_message_iter_get_basic( &invalid_props_iter, &invalid_prop_name ); - assert( invalid_prop_name != NULL ); - for (i = 0; i < ARRAY_SIZE( device_props_mask_map ); i++) - { - if (strcmp(device_props_mask_map[i].prop_str, invalid_prop_name) == 0) - props_changed.invalid_props_mask |= device_props_mask_map[i].prop_mask; - } - p_dbus_message_iter_next( &invalid_props_iter ); - } + props_changed.invalid_props_mask = bluez_dbus_get_invalidated_properties_from_iter( + &invalid_props_iter, device_prop_masks, ARRAY_SIZE( device_prop_masks ) );
TRACE( "Properties changed for device %s radio %s, changed %#x, invalidated %#x\n", debugstr_a( device->str ), debugstr_a( radio->str ), diff --git a/dlls/winebth.sys/winebluetooth.h b/dlls/winebth.sys/winebluetooth.h index ff471855edb..b043fb760bd 100644 --- a/dlls/winebth.sys/winebluetooth.h +++ b/dlls/winebth.sys/winebluetooth.h @@ -165,6 +165,7 @@ enum winebluetooth_watcher_event_type BLUETOOTH_WATCHER_EVENT_TYPE_NONE, BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED, BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_REMOVED, + BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_PROPERTIES_CHANGED, BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_ADDED, BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_REMOVED, BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_PROPERTIES_CHANGED, @@ -233,10 +234,21 @@ struct winebluetooth_watcher_event_radio_added winebluetooth_radio_t radio; };
+struct winebluetooth_watcher_event_radio_props_changed +{ + winebluetooth_radio_props_mask_t changed_props_mask; + struct winebluetooth_radio_properties props; + + winebluetooth_radio_props_mask_t invalid_props_mask; + winebluetooth_radio_t radio; +}; + union winebluetooth_watcher_event_data { struct winebluetooth_watcher_event_radio_added radio_added; winebluetooth_radio_t radio_removed; + struct winebluetooth_watcher_event_radio_props_changed radio_props_changed; + struct winebluetooth_watcher_event_device_added device_added; struct winebluetooth_watcher_event_device_removed device_removed; struct winebluetooth_watcher_event_device_props_changed device_props_changed; diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index 144bf546ba7..503366633b1 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -237,6 +237,32 @@ static void remove_bluetooth_radio( winebluetooth_radio_t radio ) winebluetooth_radio_free( radio ); }
+static void bluetooth_radio_set_properties( DEVICE_OBJECT *obj, + winebluetooth_radio_props_mask_t mask, + struct winebluetooth_radio_properties *props ); + +static void update_bluetooth_radio_properties( struct winebluetooth_watcher_event_radio_props_changed event ) +{ + struct bluetooth_radio *device; + winebluetooth_radio_t radio = event.radio; + winebluetooth_radio_props_mask_t mask = event.changed_props_mask; + struct winebluetooth_radio_properties props = event.props; + + EnterCriticalSection( &device_list_cs ); + LIST_FOR_EACH_ENTRY( device, &device_list, struct bluetooth_radio, entry ) + { + if (winebluetooth_radio_equal( radio, device->radio ) && !device->removed) + { + device->props_mask = mask; + device->props = props; + bluetooth_radio_set_properties( device->device_obj, device->props_mask, + &device->props ); + break; + } + } + LeaveCriticalSection( &device_list_cs ); +} + static void add_remote_bluetooth_device( struct winebluetooth_watcher_event_device_added event ) { struct bluetooth_radio *device; @@ -323,6 +349,9 @@ static DWORD CALLBACK bluetooth_event_loop_thread_proc( void *arg ) case BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_REMOVED: remove_bluetooth_radio( event->event_data.radio_removed ); break; + case BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_PROPERTIES_CHANGED: + update_bluetooth_radio_properties( event->event_data.radio_props_changed ); + break; case BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_ADDED: add_remote_bluetooth_device( event->event_data.device_added ); break; @@ -460,6 +489,78 @@ static NTSTATUS query_id(const struct bluetooth_radio *ext, IRP *irp, BUS_QUERY_ return STATUS_SUCCESS; }
+static void bluetooth_radio_set_properties( DEVICE_OBJECT *obj, + winebluetooth_radio_props_mask_t mask, + struct winebluetooth_radio_properties *props ) +{ + if (mask & WINEBLUETOOTH_RADIO_PROPERTY_ADDRESS) + { + union + { + UINT64 uint; + BYTE addr[8]; + } radio_addr = {0}; + memcpy(&radio_addr.addr[2], props->address.rgBytes, sizeof( props->address.rgBytes )); + IoSetDevicePropertyData( obj, &DEVPKEY_BluetoothRadio_Address, LOCALE_NEUTRAL, 0, + DEVPROP_TYPE_UINT64, sizeof( radio_addr ), &radio_addr ); + } + if (mask & WINEBLUETOOTH_RADIO_PROPERTY_MANUFACTURER) + { + UINT16 manufacturer = props->manufacturer; + IoSetDevicePropertyData( obj, &DEVPKEY_BluetoothRadio_Manufacturer, LOCALE_NEUTRAL, + 0, DEVPROP_TYPE_UINT16, sizeof( manufacturer ), &manufacturer ); + } + if (mask & WINEBLUETOOTH_RADIO_PROPERTY_DISCOVERABLE) + { + BOOL discoverable = props->discoverable; + IoSetDevicePropertyData( obj, &DEVPKEY_WineBluetooth_Radio_Discoverable, LOCALE_NEUTRAL, 0, + DEVPROP_TYPE_BOOLEAN, sizeof( BOOL ), &discoverable ); + } + if (mask & WINEBLUETOOTH_RADIO_PROPERTY_PAIRABLE) + { + BOOL discoverable = props->pairable; + IoSetDevicePropertyData( obj, &DEVPKEY_WineBluetooth_Radio_Pairable, LOCALE_NEUTRAL, 0, + DEVPROP_TYPE_BOOLEAN, sizeof( BOOL ), &discoverable ); + } + if (mask & WINEBLUETOOTH_RADIO_PROPERTY_CONNECTABLE) + { + BOOL connectable = props->connectable; + IoSetDevicePropertyData( obj, &DEVPKEY_WineBluetooth_Radio_Connectable, LOCALE_NEUTRAL, 0, + DEVPROP_TYPE_BOOLEAN, sizeof( BOOL ), &connectable ); + } + if (mask & WINEBLUETOOTH_RADIO_PROPERTY_DISCOVERING) + { + BOOL discovering = props->discovering; + IoSetDevicePropertyData( obj, &DEVPKEY_WineBluetooth_Radio_Discovering, LOCALE_NEUTRAL, 0, + DEVPROP_TYPE_BOOLEAN, sizeof( BOOL ), &discovering ); + } + if (mask & WINEBLUETOOTH_RADIO_PROPERTY_CLASS) + { + UINT32 class = props->class; + IoSetDevicePropertyData( obj, &DEVPKEY_WineBluetooth_Radio_Class, LOCALE_NEUTRAL, 0, + DEVPROP_TYPE_UINT32, sizeof( UINT32 ), &class ); + } + if (mask & WINEBLUTOOTH_RADIO_PROPERTY_NAME) + { + WCHAR *name; + size_t count = mbstowcs( NULL, props->name, 0 ); + size_t size = min( count + 1, BLUETOOTH_MAX_NAME_SIZE * sizeof( WCHAR ) ); + if (count == -1) + { + ERR( "error converting Bluetooth radio name to wide string: %s\n", + debugstr_a( props->name ) ); + return; + } + + name = malloc( size ); + mbstowcs( name, props->name, size ); + name[size - 1] = '\0'; + IoSetDevicePropertyData( obj, &DEVPKEY_Device_FriendlyName, LOCALE_NEUTRAL, 0, + DEVPROP_TYPE_STRING, size, &name ); + free( name ); + } +} + static NTSTATUS WINAPI pdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) { IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp); @@ -510,28 +611,7 @@ static NTSTATUS WINAPI pdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) } case IRP_MN_START_DEVICE: { - union - { - UINT64 uint; - BYTE addr[8]; - } radio_addr; - - if (device->props_mask & WINEBLUETOOTH_RADIO_PROPERTY_ADDRESS) - { - memset( &radio_addr, 0, sizeof( radio_addr ) ); - memcpy( &radio_addr.addr[2], device->props.address.rgBytes, - sizeof( device->props.address.rgBytes ) ); - IoSetDevicePropertyData( device_obj, &DEVPKEY_BluetoothRadio_Address, - LOCALE_NEUTRAL, 0, DEVPROP_TYPE_UINT64, - sizeof( radio_addr ), &radio_addr ); - } - if (device->props_mask & WINEBLUETOOTH_RADIO_PROPERTY_MANUFACTURER) - { - UINT16 manufacturer = device->props.manufacturer; - IoSetDevicePropertyData( device_obj, &DEVPKEY_BluetoothRadio_Manufacturer, - LOCALE_NEUTRAL, 0, DEVPROP_TYPE_UINT16, - sizeof( manufacturer ), &manufacturer ); - } + bluetooth_radio_set_properties( device_obj, device->props_mask, &device->props );
if (IoRegisterDeviceInterface( device_obj, &GUID_BTHPORT_DEVICE_INTERFACE, NULL, &device->bthport_symlink_name ) == STATUS_SUCCESS) diff --git a/include/ddk/bthguid.h b/include/ddk/bthguid.h index ab6e6a7e112..3212b78f5eb 100644 --- a/include/ddk/bthguid.h +++ b/include/ddk/bthguid.h @@ -77,6 +77,16 @@ DEFINE_WINEBTH_DEVICE_DEVPROPKEY( Name, 1 );
/* Authentication I/O capability as currently advertised by the radio. DEVPROP_TYPE_BYTE */ DEFINE_WINEBTH_RADIO_DEVPROPKEY( Capability, 1 ); +/* If device discovery is active for this radio. DEVPROP_TYPE_BOOLEAN */ +DEFINE_WINEBTH_RADIO_DEVPROPKEY( Discovering, 2 ); +/* If the radio is pariable. DEVPROP_TYPE_BOOLEAN */ +DEFINE_WINEBTH_RADIO_DEVPROPKEY( Pairable, 3 ); +/* If the radio is discoverable/visible. DEVPROP_TYPE_BOOLEAN */ +DEFINE_WINEBTH_RADIO_DEVPROPKEY( Discoverable, 4 ); +/* If the radio is connectable. DEVPROP_TYPE_BOOLEAN */ +DEFINE_WINEBTH_RADIO_DEVPROPKEY( Connectable, 5 ); +/* The class of device for this radio. DEVPROP_TYPE_UINT32 */ +DEFINE_WINEBTH_RADIO_DEVPROPKEY( Class, 6 ); #endif
#undef DEFINE_BTH_RADIO_DEVPROPKEY
From: Vibhav Pant vibhavp@gmail.com
--- dlls/winebth.sys/winebth.c | 65 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+)
diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index 503366633b1..f80336cec91 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -87,10 +87,75 @@ static NTSTATUS WINAPI dispatch_bluetooth(DEVICE_OBJECT *device, IRP *irp ) { 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;
TRACE( "device %p irp %p code %s\n", device, irp, debugstr_IoControlCode( code ) );
+ switch (code) + { + case IOCTL_BTH_GET_LOCAL_INFO: + { + BTH_LOCAL_RADIO_INFO *info = (BTH_LOCAL_RADIO_INFO *)irp->AssociatedIrp.SystemBuffer; + BOOL boolean; + WCHAR name[BLUETOOTH_MAX_NAME_SIZE]; + + if (info == NULL) + { + status = STATUS_INVALID_PARAMETER; + break; + } + if (insize < sizeof(*info) || outsize < sizeof(*info)) + { + status = STATUS_BUFFER_TOO_SMALL; + break; + } + + memset( info, 0, sizeof( *info ) ); + status = + IoGetDevicePropertyData( device, &DEVPKEY_BluetoothRadio_Address, LOCALE_NEUTRAL, 0, + sizeof( UINT64 ), &info->localInfo.address, NULL, NULL ); + if (status == STATUS_SUCCESS) info->localInfo.flags |= BDIF_ADDRESS; + + status = IoGetDevicePropertyData( device, &DEVPKEY_WineBluetooth_Radio_Class, + LOCALE_NEUTRAL, 0, sizeof( UINT32 ), + &info->localInfo.classOfDevice, NULL, NULL ); + if (status == STATUS_SUCCESS) info->localInfo.flags |= BDIF_COD; + + status = IoGetDevicePropertyData( device, &DEVPKEY_Device_FriendlyName, LOCALE_NEUTRAL, + 0, sizeof( name ), &name, NULL, NULL ); + if (status == STATUS_SUCCESS) + { + size_t size = wcstombs( info->localInfo.name, name, sizeof( info->localInfo.name ) ); + if (size != -1) + { + info->localInfo.flags |= BDIF_NAME; + info->localInfo.name[size-1] = '\0'; + } + } + + status = + IoGetDevicePropertyData( device, &DEVPKEY_WineBluetooth_Radio_Connectable, + LOCALE_NEUTRAL, 0, sizeof( BOOL ), &boolean, NULL, NULL ); + if (status == STATUS_SUCCESS && boolean) info->flags |= LOCAL_RADIO_CONNECTABLE; + + status = + IoGetDevicePropertyData( device, &DEVPKEY_WineBluetooth_Radio_Discoverable, + LOCALE_NEUTRAL, 0, sizeof( BOOL ), &boolean, NULL, NULL ); + if (status == STATUS_SUCCESS && boolean) info->flags |= LOCAL_RADIO_DISCOVERABLE; + + IoGetDevicePropertyData( device, &DEVPKEY_BluetoothRadio_Manufacturer, LOCALE_NEUTRAL, + 0, sizeof( UINT16 ), &info->radioInfo.mfg, NULL, NULL ); + + status = STATUS_SUCCESS; + irp->IoStatus.Information = sizeof( *info ); + } + default: + break; + } + + irp->IoStatus.Status = status; IoCompleteRequest( irp, IO_NO_INCREMENT ); return status;
From: Vibhav Pant vibhavp@gmail.com
--- dlls/winebth.sys/dbus.c | 142 +++++++++++++++++++++++++++++++ dlls/winebth.sys/unixlib.c | 20 +++++ dlls/winebth.sys/unixlib.h | 14 +++ dlls/winebth.sys/unixlib_priv.h | 3 + dlls/winebth.sys/winebluetooth.c | 21 +++++ dlls/winebth.sys/winebluetooth.h | 4 + dlls/winebth.sys/winebth.c | 31 ++++++- include/bthioctl.h | 12 +++ 8 files changed, 246 insertions(+), 1 deletion(-)
diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index de083925513..1fb2414aa9b 100644 --- a/dlls/winebth.sys/dbus.c +++ b/dlls/winebth.sys/dbus.c @@ -196,6 +196,22 @@ static const char *bluez_next_dict_entry( DBusMessageIter *iter, DBusMessageIter return name; }
+/* Adds an entry to a dict iterator of type {sv} */ +static void bluez_variant_dict_add_entry( DBusMessageIter *dict, const char *key, int value_type, + const char *value_type_str, const void *value ) +{ + DBusMessageIter entry, variant; + + p_dbus_message_iter_open_container( dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry ); + p_dbus_message_iter_append_basic( &entry, DBUS_TYPE_STRING, &key ); + + p_dbus_message_iter_open_container( &entry, DBUS_TYPE_VARIANT, value_type_str, &variant ); + p_dbus_message_iter_append_basic( &variant, value_type, value ); + p_dbus_message_iter_close_container( &entry, &variant ); + + p_dbus_message_iter_close_container( dict, &entry ); +} + static const char *dbgstr_dbus_message( DBusMessage *message ) { const char *interface; @@ -289,6 +305,132 @@ static void parse_mac_address( const char *addr_str, BYTE dest[6] ) dest[i] = addr[i]; }
+static NTSTATUS bluez_adapter_set_discovery_filter( void *connection, const char *adapter_path, + const char *transport_str ) +{ + DBusMessage *request, *reply; + DBusMessageIter iter, dict_iter; + DBusError error; + + TRACE( "(%p, %s, %s)\n", connection, debugstr_a( adapter_path ), debugstr_a( transport_str ) ); + + request = p_dbus_message_new_method_call( BLUEZ_DEST, adapter_path, BLUEZ_INTERFACE_ADAPTER, + "SetDiscoveryFilter" ); + if (!request) return STATUS_NO_MEMORY; + + p_dbus_message_iter_init_append( request, &iter ); + p_dbus_message_iter_open_container( &iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter ); + bluez_variant_dict_add_entry( &dict_iter, "Transport", DBUS_TYPE_STRING, + DBUS_TYPE_STRING_AS_STRING, &transport_str ); + p_dbus_message_iter_close_container( &iter, &dict_iter ); + + p_dbus_error_init ( &error ); + + TRACE( "Setting discovery filter on %s to use transport %s\n", debugstr_a( adapter_path ), + debugstr_a( transport_str ) ); + reply = + p_dbus_connection_send_with_reply_and_block( connection, request, bluez_timeout, &error ); + p_dbus_message_unref( request ); + if (!reply) + { + NTSTATUS status; + WARN( "Failed to set discovery filter: %s: %s\n", debugstr_a( error.name ), + debugstr_a( error.message ) ); + status = bluez_dbus_error_to_ntstatus( &error ); + p_dbus_error_free( &error ); + return status; + } + p_dbus_message_unref( reply ); + p_dbus_error_free( &error ); + + return STATUS_SUCCESS; + +} + +NTSTATUS bluez_adapter_start_discovery( void *connection, const char *adapter_path, + enum winebluetooth_discovery_transport transport ) +{ + DBusMessage *request, *reply; + DBusError error; + + TRACE( "(%p, %s, %d)\n", connection, debugstr_a( adapter_path ), transport ); + + if (transport != WINEBLUETOOTH_DISCOVERY_TRANSPORT_DEFAULT) + { + const char *transport_str; + NTSTATUS status; + + switch ( transport ) + { + case WINEBLUETOOTH_DISCOVERY_TRANSPORT_AUTO: + transport_str = "auto"; + break; + case WINEBLUETOOTH_DISCOVERY_TRANSPORT_BR_EDR: + transport_str = "bredr"; + break; + case WINEBLUETOOTH_DISCOVERY_TRANSPORT_LE: + transport_str = "le"; + break; + default: + return STATUS_INVALID_PARAMETER; + } + + status = bluez_adapter_set_discovery_filter( connection, adapter_path, transport_str ); + if (status != STATUS_SUCCESS) return status; + } + + request = p_dbus_message_new_method_call( BLUEZ_DEST, adapter_path, BLUEZ_INTERFACE_ADAPTER, + "StartDiscovery" ); + if (!request) return STATUS_NO_MEMORY; + + TRACE( "Starting discovery on %s\n", debugstr_a( adapter_path ) ); + p_dbus_error_init( &error ); + reply = + p_dbus_connection_send_with_reply_and_block( connection, request, bluez_timeout, &error ); + p_dbus_message_unref( request ); + if (!reply) + { + NTSTATUS status; + ERR( "Failed to start discovery on adapter %s: %s: %s", debugstr_a( adapter_path ), + debugstr_a( error.message ), debugstr_a( error.name ) ); + 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; +} + +NTSTATUS bluez_adapter_stop_discovery( void *connection, const char *adapter_path ) +{ + DBusMessage *request, *reply; + DBusError error; + + TRACE( "(%p, %s)\n", connection, debugstr_a( adapter_path ) ); + + request = p_dbus_message_new_method_call( BLUEZ_DEST, adapter_path, BLUEZ_INTERFACE_ADAPTER, + "StopDiscovery" ); + if (!request) return STATUS_NO_MEMORY; + + p_dbus_error_init( &error ); + reply = + p_dbus_connection_send_with_reply_and_block( connection, request, bluez_timeout, &error ); + p_dbus_message_unref( request ); + if (!reply) + { + NTSTATUS status; + ERR( "Failed to stop discovery on adapter %s: %s: %s", debugstr_a( adapter_path ), + debugstr_a( error.message ), debugstr_a( error.name ) ); + 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, diff --git a/dlls/winebth.sys/unixlib.c b/dlls/winebth.sys/unixlib.c index 79fdb101648..df8af51afe2 100644 --- a/dlls/winebth.sys/unixlib.c +++ b/dlls/winebth.sys/unixlib.c @@ -153,6 +153,23 @@ static NTSTATUS bluetooth_adapter_free( void *args ) return STATUS_SUCCESS; }
+static NTSTATUS bluetooth_adapter_start_discovery( void *args ) +{ + struct bluetooth_adapter_start_discovery_params *params = args; + + if (!dbus_connection) return STATUS_NOT_SUPPORTED; + return bluez_adapter_start_discovery( dbus_connection, params->adapter->str, + params->transport ); +} + +static NTSTATUS bluetooth_adapter_stop_discovery( void *args ) +{ + struct bluetooth_adapter_stop_discovery_params *params = args; + + if (!dbus_connection) return STATUS_NOT_SUPPORTED; + return bluez_adapter_stop_discovery( dbus_connection, params->adapter->str ); +} + static NTSTATUS bluetooth_watcher_init( void *args ) { struct bluetooth_watcher_init_params *params = args; @@ -201,6 +218,9 @@ const unixlib_entry_t __wine_unix_call_funcs[] = { bluetooth_adapter_get_unique_name, bluetooth_adapter_free,
+ bluetooth_adapter_start_discovery, + bluetooth_adapter_stop_discovery, + bluetooth_device_free,
bluetooth_watcher_init, diff --git a/dlls/winebth.sys/unixlib.h b/dlls/winebth.sys/unixlib.h index 15fd76c1366..953d916d6b5 100644 --- a/dlls/winebth.sys/unixlib.h +++ b/dlls/winebth.sys/unixlib.h @@ -70,6 +70,17 @@ struct bluetooth_adapter_get_unique_name_params SIZE_T buf_size; };
+struct bluetooth_adapter_start_discovery_params +{ + unix_name_t adapter; + enum winebluetooth_discovery_transport transport; +}; + +struct bluetooth_adapter_stop_discovery_params +{ + unix_name_t adapter; +}; + struct bluetooth_get_device_props_by_path_params { unix_name_t device_path; @@ -124,6 +135,9 @@ enum bluetoothapis_funcs unix_bluetooth_adapter_get_unique_name, unix_bluetooth_adapter_free,
+ unix_bluetooth_adapter_start_discovery, + unix_bluetooth_adapter_stop_discovery, + unix_bluetooth_device_free,
unix_bluetooth_watcher_init, diff --git a/dlls/winebth.sys/unixlib_priv.h b/dlls/winebth.sys/unixlib_priv.h index 0690e86f99b..593c16c743a 100644 --- a/dlls/winebth.sys/unixlib_priv.h +++ b/dlls/winebth.sys/unixlib_priv.h @@ -49,6 +49,9 @@ extern void *bluez_dbus_init( void ); extern void bluez_dbus_close( void *connection ); extern void bluez_dbus_free( void *connection ); extern NTSTATUS bluez_dbus_loop( void *connection, void *watcher_ctx, struct winebluetooth_event_loop_result *result ); +extern NTSTATUS bluez_adapter_start_discovery( void *connection, const char *adapter_path, + enum winebluetooth_discovery_transport transport ); +extern NTSTATUS bluez_adapter_stop_discovery( void *connection, const char *adapter_path ); extern NTSTATUS bluez_watcher_init( void *connection, void **ctx ); extern void bluez_watcher_close( void *connection, void *ctx ); #endif /* SONAME_LIBDBUS_1 */ diff --git a/dlls/winebth.sys/winebluetooth.c b/dlls/winebth.sys/winebluetooth.c index 0cc499d7acb..f3d907cb850 100644 --- a/dlls/winebth.sys/winebluetooth.c +++ b/dlls/winebth.sys/winebluetooth.c @@ -55,6 +55,27 @@ NTSTATUS winebluetooth_radio_get_unique_name( winebluetooth_radio_t radio, char return status; }
+NTSTATUS winebluetooth_radio_start_discovery( + winebluetooth_radio_t radio, enum winebluetooth_discovery_transport transport ) +{ + struct bluetooth_adapter_start_discovery_params params = {0}; + + TRACE( "(%p, %d)\n", radio, transport ); + + params.adapter = radio.handle; + params.transport = transport; + return UNIX_BLUETOOTH_CALL( bluetooth_adapter_start_discovery, ¶ms ); +} + +NTSTATUS winebluetooth_radio_stop_discovery( winebluetooth_radio_t radio ) +{ + struct bluetooth_adapter_stop_discovery_params params = {0}; + + TRACE( "(%p)\n", radio.handle ); + params.adapter = radio.handle; + return UNIX_BLUETOOTH_CALL(bluetooth_adapter_stop_discovery, ¶ms); +} + 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 b043fb760bd..1d7bc4a6733 100644 --- a/dlls/winebth.sys/winebluetooth.h +++ b/dlls/winebth.sys/winebluetooth.h @@ -118,6 +118,7 @@ struct winebluetooth_device_properties
enum winebluetooth_discovery_transport { + WINEBLUETOOTH_DISCOVERY_TRANSPORT_DEFAULT, WINEBLUETOOTH_DISCOVERY_TRANSPORT_AUTO, WINEBLUETOOTH_DISCOVERY_TRANSPORT_BR_EDR, WINEBLUETOOTH_DISCOVERY_TRANSPORT_LE @@ -140,6 +141,9 @@ static inline BOOL winebluetooth_radio_equal( winebluetooth_radio_t r1, wineblue { return r1.handle == r2.handle; } +NTSTATUS winebluetooth_radio_start_discovery( + winebluetooth_radio_t radio, enum winebluetooth_discovery_transport transport ); +NTSTATUS winebluetooth_radio_stop_discovery( winebluetooth_radio_t radio );
void winebluetooth_device_free( winebluetooth_device_t device ); static inline BOOL winebluetooth_device_equal( winebluetooth_device_t d1, diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index f80336cec91..915ac4a62cf 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -85,6 +85,7 @@ struct bluetooth_radio
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; @@ -151,10 +152,38 @@ static NTSTATUS WINAPI dispatch_bluetooth(DEVICE_OBJECT *device, IRP *irp ) status = STATUS_SUCCESS; irp->IoStatus.Information = sizeof( *info ); } + case IOCTL_WINEBTH_RADIO_START_DISCOVERY: + { + struct winebth_discovery_filter *filter = + (struct winebth_discovery_filter *)irp->AssociatedIrp.SystemBuffer; + enum winebluetooth_discovery_transport transport; + if (filter == NULL) + { + status = STATUS_INVALID_PARAMETER; + break; + } + if (insize < sizeof(*filter)) + { + status = STATUS_BUFFER_TOO_SMALL; + break; + } + + if (filter->le && filter->bredr) + transport = WINEBLUETOOTH_DISCOVERY_TRANSPORT_AUTO; + else if (filter->le) + transport = WINEBLUETOOTH_DISCOVERY_TRANSPORT_LE; + else if (filter->bredr) + transport = WINEBLUETOOTH_DISCOVERY_TRANSPORT_BR_EDR; + else + transport = WINEBLUETOOTH_DISCOVERY_TRANSPORT_DEFAULT; + status = winebluetooth_radio_start_discovery( ext->radio, transport ); + } + case IOCTL_WINEBTH_RADIO_STOP_DISCOVERY: + status = winebluetooth_radio_stop_discovery( ext->radio ); default: break; } - +
irp->IoStatus.Status = status; IoCompleteRequest( irp, IO_NO_INCREMENT ); diff --git a/include/bthioctl.h b/include/bthioctl.h index 773ea54f154..5b697f47382 100644 --- a/include/bthioctl.h +++ b/include/bthioctl.h @@ -41,6 +41,11 @@ #define IOCTL_INTERNAL_BTHENUM_GET_ENUMINFO CTL_CODE(FILE_DEVICE_BLUETOOTH, 0x01, METHOD_NEITHER, FILE_ANY_ACCESS) #define IOCTL_INTERNAL_BTHENUM_GET_DEVINFO CTL_CODE(FILE_DEVICE_BLUETOOTH, 0x02, METHOD_NEITHER, FILE_ANY_ACCESS)
+#ifdef WINE_BTH_EXTENSIONS +#define IOCTL_WINEBTH_RADIO_START_DISCOVERY CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xa6, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_WINEBTH_RADIO_STOP_DISCOVERY CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xa7, METHOD_BUFFERED, FILE_ANY_ACCESS) +#endif + #define LOCAL_RADIO_DISCOVERABLE (0x0001) #define LOCAL_RADIO_CONNECTABLE (0x0002)
@@ -67,5 +72,12 @@ typedef struct _BTH_DEVICE_INFO_LIST BTH_DEVICE_INFO deviceList[1]; } BTH_DEVICE_INFO_LIST, *PBTH_DEVICE_INFO_LIST;
+#ifdef WINE_BTH_EXTENSIONS +struct winebth_discovery_filter +{ + unsigned int bredr : 1; + unsigned int le : 1; +}; +#endif #endif /* __WINE_BTHIOCTL_H_ */
From: Vibhav Pant vibhavp@gmail.com
--- dlls/winebth.sys/dbus.c | 67 ++++++++++++++++++++++++++++++++ dlls/winebth.sys/unixlib.c | 9 +++++ dlls/winebth.sys/unixlib.h | 9 +++++ dlls/winebth.sys/unixlib_priv.h | 2 + dlls/winebth.sys/winebluetooth.c | 30 +++++++++++++- dlls/winebth.sys/winebluetooth.h | 3 ++ dlls/winebth.sys/winebth.c | 33 ++++++++++++++++ include/bthioctl.h | 2 + 8 files changed, 154 insertions(+), 1 deletion(-)
diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index 1fb2414aa9b..5f5f5d4c2d3 100644 --- a/dlls/winebth.sys/dbus.c +++ b/dlls/winebth.sys/dbus.c @@ -305,6 +305,73 @@ static void parse_mac_address( const char *addr_str, BYTE dest[6] ) dest[i] = addr[i]; }
+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; + + TRACE( "(%p, %p)\n", connection, params ); + + switch (params->prop_flag) + { + case WINEBLUETOOTH_RADIO_PROPERTY_DISCOVERABLE: + prop_name = "Discoverable"; + val.bool_val = params->prop->bool; + val_type = DBUS_TYPE_BOOLEAN; + break; + case WINEBLUETOOTH_RADIO_PROPERTY_CONNECTABLE: + prop_name = "Connectable"; + val.bool_val = params->prop->bool; + val_type = DBUS_TYPE_BOOLEAN; + break; + case WINEBLUETOOTH_RADIO_PROPERTY_PAIRABLE: + prop_name = "Pairable"; + val.bool_val = params->prop->bool; + 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 ); + p_dbus_message_iter_append_basic( &iter, DBUS_TYPE_STRING, &adapter_iface ); + p_dbus_message_iter_append_basic( &iter, DBUS_TYPE_STRING, &prop_name ); + p_dbus_message_iter_open_container( &iter, DBUS_TYPE_VARIANT, DBUS_TYPE_BOOLEAN_AS_STRING, + &sub_iter ); + p_dbus_message_iter_append_basic( &sub_iter, val_type, &val ); + p_dbus_message_iter_close_container( &iter, &sub_iter ); + + p_dbus_error_init( &error ); + reply = + p_dbus_connection_send_with_reply_and_block( connection, request, bluez_timeout, &error ); + p_dbus_message_unref( request ); + if (!reply) + { + NTSTATUS status; + 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 NTSTATUS bluez_adapter_set_discovery_filter( void *connection, const char *adapter_path, const char *transport_str ) { diff --git a/dlls/winebth.sys/unixlib.c b/dlls/winebth.sys/unixlib.c index df8af51afe2..017e073207e 100644 --- a/dlls/winebth.sys/unixlib.c +++ b/dlls/winebth.sys/unixlib.c @@ -153,6 +153,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_adapter_start_discovery( void *args ) { struct bluetooth_adapter_start_discovery_params *params = args; @@ -215,6 +223,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 953d916d6b5..ba468e54b19 100644 --- a/dlls/winebth.sys/unixlib.h +++ b/dlls/winebth.sys/unixlib.h @@ -70,6 +70,14 @@ struct bluetooth_adapter_get_unique_name_params SIZE_T buf_size; };
+struct bluetooth_adapter_set_prop_params +{ + unix_name_t adapter; + winebluetooth_radio_props_mask_t prop_flag; + + union winebluetooth_property *prop; +}; + struct bluetooth_adapter_start_discovery_params { unix_name_t adapter; @@ -132,6 +140,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 593c16c743a..2375aee531f 100644 --- a/dlls/winebth.sys/unixlib_priv.h +++ b/dlls/winebth.sys/unixlib_priv.h @@ -49,6 +49,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_loop_result *result ); +extern NTSTATUS bluez_adapter_set_prop( void *connection, + struct bluetooth_adapter_set_prop_params *params ); extern NTSTATUS bluez_adapter_start_discovery( void *connection, const char *adapter_path, enum winebluetooth_discovery_transport transport ); extern NTSTATUS bluez_adapter_stop_discovery( void *connection, const char *adapter_path ); diff --git a/dlls/winebth.sys/winebluetooth.c b/dlls/winebth.sys/winebluetooth.c index f3d907cb850..525db1fa2e1 100644 --- a/dlls/winebth.sys/winebluetooth.c +++ b/dlls/winebth.sys/winebluetooth.c @@ -55,6 +55,34 @@ NTSTATUS winebluetooth_radio_get_unique_name( winebluetooth_radio_t radio, char return status; }
+NTSTATUS winebluetooth_radio_set_property( winebluetooth_radio_t radio, + winebluetooth_radio_props_mask_t prop, + union winebluetooth_property *property ) +{ + struct bluetooth_adapter_set_prop_params params = { 0 }; + NTSTATUS status; + + TRACE( "(%p, %#x, %p)\n", radio.handle, prop, property ); + + params.adapter = radio.handle; + + switch (prop) + { + case WINEBLUETOOTH_RADIO_PROPERTY_CONNECTABLE: + case WINEBLUETOOTH_RADIO_PROPERTY_DISCOVERABLE: + case WINEBLUETOOTH_RADIO_PROPERTY_PAIRABLE: + break; + default: + return STATUS_INVALID_PARAMETER; + } + + status = UNIX_BLUETOOTH_CALL( bluetooth_adapter_set_prop, ¶ms ); + + if (status != STATUS_SUCCESS) return status; + + return STATUS_SUCCESS; +} + NTSTATUS winebluetooth_radio_start_discovery( winebluetooth_radio_t radio, enum winebluetooth_discovery_transport transport ) { @@ -111,7 +139,7 @@ NTSTATUS winebluetooth_guid_array_free( UINT_PTR src )
TRACE( "(%p)\n", src ); params.arr = src; - return UNIX_BLUETOOTH_CALL( bluetooth_guid_array_free, ¶ms ); + return UNIX_BLUETOOTH_CALL( bluetooth_guid_array_free, ¶ms ); }
NTSTATUS winebluetooth_watcher_init( winebluetooth_watcher_t *watcher ) diff --git a/dlls/winebth.sys/winebluetooth.h b/dlls/winebth.sys/winebluetooth.h index 1d7bc4a6733..f3cf220ae31 100644 --- a/dlls/winebth.sys/winebluetooth.h +++ b/dlls/winebth.sys/winebluetooth.h @@ -141,6 +141,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, + winebluetooth_radio_props_mask_t prop, + union winebluetooth_property *property ); NTSTATUS winebluetooth_radio_start_discovery( winebluetooth_radio_t radio, enum winebluetooth_discovery_transport transport ); NTSTATUS winebluetooth_radio_stop_discovery( winebluetooth_radio_t radio ); diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index 915ac4a62cf..1f7adfb4eb0 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -152,6 +152,39 @@ static NTSTATUS WINAPI dispatch_bluetooth(DEVICE_OBJECT *device, IRP *irp ) status = STATUS_SUCCESS; irp->IoStatus.Information = sizeof( *info ); } + case IOCTL_WINEBTH_RADIO_SET_FLAG: + case IOCTL_WINEBTH_RADIO_UNSET_FLAG: + { + UINT32 *flags = (UINT32 *)irp->AssociatedIrp.SystemBuffer; + union winebluetooth_property prop_value = {.bool = code == IOCTL_WINEBTH_RADIO_SET_FLAG}; + + if (flags == NULL) + { + status = STATUS_INVALID_PARAMETER; + break; + } + if (insize < sizeof(UINT32)) + { + status = STATUS_BUFFER_TOO_SMALL; + break; + } + + TRACE( "flags=%s\n", debugstr_radio_flags( *flags ) ); + switch (*flags) + { + case LOCAL_RADIO_CONNECTABLE: + status = winebluetooth_radio_set_property( + ext->radio, WINEBLUETOOTH_RADIO_PROPERTY_CONNECTABLE, &prop_value ); + break; + case LOCAL_RADIO_DISCOVERABLE: + status = winebluetooth_radio_set_property( + ext->radio, WINEBLUETOOTH_RADIO_PROPERTY_DISCOVERABLE, &prop_value ); + break; + default: + status = STATUS_INVALID_PARAMETER; + } + break; + } case IOCTL_WINEBTH_RADIO_START_DISCOVERY: { struct winebth_discovery_filter *filter = diff --git a/include/bthioctl.h b/include/bthioctl.h index 5b697f47382..0b9cc6164a4 100644 --- a/include/bthioctl.h +++ b/include/bthioctl.h @@ -42,6 +42,8 @@ #define IOCTL_INTERNAL_BTHENUM_GET_DEVINFO CTL_CODE(FILE_DEVICE_BLUETOOTH, 0x02, METHOD_NEITHER, FILE_ANY_ACCESS)
#ifdef WINE_BTH_EXTENSIONS +#define IOCTL_WINEBTH_RADIO_SET_FLAG CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xa3, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_WINEBTH_RADIO_UNSET_FLAG CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xa4, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_WINEBTH_RADIO_START_DISCOVERY CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xa6, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_WINEBTH_RADIO_STOP_DISCOVERY CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xa7, METHOD_BUFFERED, FILE_ANY_ACCESS) #endif
From: Vibhav Pant vibhavp@gmail.com
The event is sent when a remote Bluetooth device is either discovered during device inquiry, or when the its known properties have changed. --- dlls/winebth.sys/bthenum.c | 44 +++++++++++++++++++++++++++++++++++++- include/bthdef.h | 9 ++++++++ 2 files changed, 52 insertions(+), 1 deletion(-)
diff --git a/dlls/winebth.sys/bthenum.c b/dlls/winebth.sys/bthenum.c index 7e55cff1219..2d9ed16c070 100644 --- a/dlls/winebth.sys/bthenum.c +++ b/dlls/winebth.sys/bthenum.c @@ -220,14 +220,49 @@ static UINT32 device_get_flags( winebluetooth_device_props_mask_t props_mask, static BOOL device_set_properties( DEVICE_OBJECT *device_obj, UINT32 *old_flags, winebluetooth_device_props_mask_t props_mask, struct winebluetooth_device_properties props ); +static void device_get_properties( DEVICE_OBJECT *device_obj, BTH_DEVICE_INFO *info ); + +static void device_send_radio_in_range_event( DEVICE_OBJECT *device_obj, UINT32 old_flags ) +{ + TARGET_DEVICE_CUSTOM_NOTIFICATION *notification; + DWORD size = offsetof( TARGET_DEVICE_CUSTOM_NOTIFICATION, + CustomDataBuffer[sizeof( BTH_RADIO_IN_RANGE )] ); + BTH_RADIO_IN_RANGE *device_data; + struct bthenum_phys_remote_radio_ext *device_ext = + (struct bthenum_phys_remote_radio_ext *)device_obj->DeviceExtension; + NTSTATUS status; + + notification = malloc( size ); + if (!notification) + { + ERR( "Could not allocate memory for BLUETOOTH_RADIO_IN_RANGE event.\n" ); + return; + } + + notification->Version = 1; + notification->Size = size; + notification->Event = GUID_BLUETOOTH_RADIO_IN_RANGE; + notification->FileObject = NULL; + notification->NameBufferOffset = -1; + device_data = (BTH_RADIO_IN_RANGE *)notification->CustomDataBuffer; + device_data->previousDeviceFlags = old_flags; + device_get_properties( device_obj, &device_data->deviceInfo ); + + status = + IoReportTargetDeviceChange( device_ext->base.parent_fdo->parent_radio_pdo, notification ); + if (status) + ERR( "Could not send BLUETOOTH_RADIO_IN_RANGE notification: %#x\n", status ); + free( notification ); +}
static void device_refresh_properties( struct bthenum_phys_remote_radio_ext *remote_device, struct winebluetooth_device_properties new_props, - winebluetooth_device_props_mask_t changed_props) + winebluetooth_device_props_mask_t changed_props ) { UINT32 old_flags = device_get_flags( remote_device->known_props, remote_device->props ); device_set_properties( remote_device->device_obj, &old_flags, changed_props, new_props );
+ device_send_radio_in_range_event( remote_device->device_obj, old_flags ); return; }
@@ -721,6 +756,7 @@ static NTSTATUS WINAPI pdo_device_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) { GUID g = GUID_NULL; GUID *guids = NULL; + BOOL discovering = FALSE;
if ( IoRegisterDeviceInterface( device->device_obj, &GUID_BTH_DEVICE_INTERFACE, NULL, &device->device_link_name ) == STATUS_SUCCESS ) @@ -741,6 +777,12 @@ static NTSTATUS WINAPI pdo_device_pnp( DEVICE_OBJECT *device_obj, IRP *irp ) } remote_device_refresh_services( device, guids, device->props.uuids_len ); if (guids) free( guids ); + /* If the radio is currently in a discovery procedure, notify userspace about the newly + * discovered device. */ + if (!IoGetDevicePropertyData( device->base.parent_fdo->parent_radio_pdo, + &DEVPKEY_WineBluetooth_Radio_Discovering, LOCALE_NEUTRAL, + 0, sizeof( BOOL ), &discovering, NULL, NULL ) && discovering) + device_send_radio_in_range_event( device_obj, 0 ); break; } case IRP_MN_SURPRISE_REMOVAL: diff --git a/include/bthdef.h b/include/bthdef.h index d431de929c9..6c200073995 100644 --- a/include/bthdef.h +++ b/include/bthdef.h @@ -49,6 +49,15 @@ typedef struct _BTH_DEVICE_INFO DEFINE_GUID( GUID_BTHPORT_DEVICE_INTERFACE, 0x850302a, 0xb344, 0x4fda, 0x9b, 0xe9, 0x90, 0x57, 0x6b, 0x8d, 0x46, 0xf0 );
+DEFINE_GUID( GUID_BLUETOOTH_RADIO_IN_RANGE, 0xea3b5b82, 0x26ee, 0x450e, 0xb0, 0xd8, 0xd2, 0x6f, + 0xe3, 0x0a, 0x38, 0x69 ); +typedef struct _BTH_RADIO_IN_RANGE +{ + BTH_DEVICE_INFO deviceInfo; + /* 0 when the device has just been discovered. */ + ULONG previousDeviceFlags; +} BTH_RADIO_IN_RANGE, *PBTH_RADIO_IN_RANGE; + #ifdef __cplusplus } #endif
From: Vibhav Pant vibhavp@gmail.com
--- dlls/winebth.sys/bthenum.c | 143 ++++++ dlls/winebth.sys/dbus.c | 844 ++++++++++++++++++++++++++++++- dlls/winebth.sys/unixlib.c | 36 +- dlls/winebth.sys/unixlib.h | 21 + dlls/winebth.sys/unixlib_priv.h | 6 +- dlls/winebth.sys/winebluetooth.c | 44 +- dlls/winebth.sys/winebluetooth.h | 40 ++ dlls/winebth.sys/winebth.c | 56 +- dlls/winebth.sys/winebth_priv.h | 2 + include/bthdef.h | 41 ++ include/bthioctl.h | 2 + 11 files changed, 1227 insertions(+), 8 deletions(-)
diff --git a/dlls/winebth.sys/bthenum.c b/dlls/winebth.sys/bthenum.c index 2d9ed16c070..b12c8d9ab94 100644 --- a/dlls/winebth.sys/bthenum.c +++ b/dlls/winebth.sys/bthenum.c @@ -116,6 +116,7 @@ struct bthenum_phys_remote_radio_ext winebluetooth_device_t remote_radio_handle; winebluetooth_device_props_mask_t known_props; struct winebluetooth_device_properties props; + UINT16 passkey_keys_entered;
UNICODE_STRING device_link_name;
@@ -160,6 +161,7 @@ NTSTATUS WINAPI bthenum_device_added( DEVICE_OBJECT *fdo_bus, child_device->props = event.props; child_device->known_props = event.known_props_mask; child_device->device_obj = child_pdo; + child_device->passkey_keys_entered = 0;
list_init( &child_device->services );
@@ -255,6 +257,147 @@ static void device_send_radio_in_range_event( DEVICE_OBJECT *device_obj, UINT32 free( notification ); }
+static NTSTATUS device_send_auth_event( DEVICE_OBJECT *radio_pdo, DEVICE_OBJECT *device_pdo, + struct winebluetooth_auth_event event ) +{ + TARGET_DEVICE_CUSTOM_NOTIFICATION *notification = NULL; + DWORD size; + NTSTATUS status; + + switch (event.event_type) + { + case BLUETOOTH_AUTH_EVENT_DEVICE_AUTH_REQUEST: + { + BTH_AUTHENTICATION_REQUEST *req; + + size = offsetof( TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer[sizeof( *req )] ); + if (event.auth_method == BLUETOOTH_AUTHENTICATION_METHOD_LEGACY) + size += sizeof( event.auth_data.pincode ); + + notification = calloc( 1, size ); + if (!notification) + { + status = STATUS_NO_MEMORY; + break; + } + + notification->Version = 1; + notification->Size = size; + notification->Event = GUID_BLUETOOTH_AUTHENTICATION_REQUEST; + notification->FileObject = NULL; + notification->NameBufferOffset = -1; + + req = (BTH_AUTHENTICATION_REQUEST *)notification->CustomDataBuffer; + device_get_properties( device_pdo, &req->deviceInfo ); + req->authenticationMethod = event.auth_method; + /* We don't have a way to get the authentication requirements for a device right now. */ + req->AuthenticationRequirements = BLUETOOTH_MITM_ProtectionNotDefined; + /* Same for IO capabilities, so we try to guess them from the authentication method. */ + switch (event.auth_method) + { + case BLUETOOTH_AUTHENTICATION_METHOD_LEGACY: + { + UCHAR *pin = (UCHAR *)¬ification->CustomDataBuffer[sizeof(*req)]; + notification->NameBufferOffset = sizeof( *req ); + memcpy( pin, event.auth_data.pincode, sizeof( event.auth_data.pincode ) ); + req->IoCapability = BLUETOOTH_IO_CAPABILITY_KEYBOARDONLY; + break; + } + case BLUETOOTH_AUTHENTICATION_METHOD_PASSKEY: + req->Passkey = event.auth_data.passkey; + req->IoCapability = BLUETOOTH_IO_CAPABILITY_KEYBOARDONLY; + break; + case BLUETOOTH_AUTHENTICATION_METHOD_NUMERIC_COMPARISON: + req->Numeric_Value = event.auth_data.passkey; + req->IoCapability = BLUETOOTH_IO_CAPABILITY_DISPLAYYESNO; + break; + case BLUETOOTH_AUTHENTICATION_METHOD_PASSKEY_NOTIFICATION: + req->Passkey = event.auth_data.passkey; + req->IoCapability = BLUETOOTH_IO_CAPABILITY_DISPLAYONLY; + break; + default: + req->IoCapability = BLUETOOTH_IO_CAPABILITY_UNDEFINED; + } + break; + } + case BLUETOOTH_AUTH_EVENT_DEVICE_KEYPRESS: + { + BTH_HCI_KEYPRESS_INFO *info; + ULONG *passkey; + struct bthenum_phys_remote_radio_ext *ext = + (struct bthenum_phys_remote_radio_ext *)device_pdo->DeviceExtension; + + size = offsetof( TARGET_DEVICE_CUSTOM_NOTIFICATION, + CustomDataBuffer[sizeof( *info ) + sizeof( ULONG )] ); + notification = calloc( 1, size ); + if (!notification) + { + status = STATUS_NO_MEMORY; + break; + } + + info = (BTH_HCI_KEYPRESS_INFO *)notification->CustomDataBuffer; + passkey = (ULONG *)¬ification->CustomDataBuffer[sizeof(*info)]; + + notification->Version = 1; + notification->Size = size; + notification->Event = GUID_BLUETOOTH_AUTHENTICATION_REQUEST; + notification->FileObject = NULL; + notification->NameBufferOffset = -1; + + *passkey = event.auth_data.passkey; + if (event.auth_data.keypress.entered > ext->passkey_keys_entered) + info->NotificationType = BTH_KEYPRESS_NOTIFICATION_PASSKEY_DIGIT_ENTERED; + else if (event.auth_data.keypress.entered < ext->passkey_keys_entered) + info->NotificationType = !event.auth_data.keypress.entered + ? BTH_KEYPRESS_NOTIFICATION_PASSKEY_CLEARED + : BTH_KEYPRESS_NOTIFICATION_PASSKEY_DIGIT_ERASED; + else if (!event.auth_data.keypress.entered) + info->NotificationType = BTH_KEYPRESS_NOTIFICATION_PASSKEY_STARTED; + else + info->NotificationType = BTH_KEYPRESS_NOTIFICATION_PASSKEY_ENTRY_COMPLETED; + ext->passkey_keys_entered = + info->NotificationType == BTH_KEYPRESS_NOTIFICATION_PASSKEY_ENTRY_COMPLETED + ? 0 + : event.auth_data.keypress.entered; + } + case BLUETOOTH_AUTH_EVENT_DEVICE_SERVICE_AUTH_REQUEST: + default: + FIXME( "Unsupported auth event: %#x\n", event.event_type ); + status = STATUS_NOT_SUPPORTED; + } + + if (notification) + { + status = IoReportTargetDeviceChange( radio_pdo, notification ); + free( notification ); + } + return status; +} + +NTSTATUS WINAPI bthenum_device_auth_event( DEVICE_OBJECT *fdo_bus, + struct winebluetooth_auth_event event ) +{ + struct bthenum_func_local_radio_ext *fdo; + struct bthenum_phys_remote_radio_ext *remote_device; + NTSTATUS status = STATUS_NOT_FOUND; + + fdo = fdo_from_DEVICE_OBJECT( fdo_bus ); + RtlEnterCriticalSection( &fdo->cs ); + LIST_FOR_EACH_ENTRY( remote_device, &fdo->devices, struct bthenum_phys_remote_radio_ext, entry ) + { + if (winebluetooth_device_equal( remote_device->remote_radio_handle, event.device )) + { + status = + device_send_auth_event( fdo->parent_radio_pdo, remote_device->device_obj, event ); + break; + } + } + RtlLeaveCriticalSection( &fdo->cs ); + winebluetooth_device_free( event.device ); + return status; +} + static void device_refresh_properties( struct bthenum_phys_remote_radio_ext *remote_device, struct winebluetooth_device_properties new_props, winebluetooth_device_props_mask_t changed_props ) diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index 5f5f5d4c2d3..31251806822 100644 --- a/dlls/winebth.sys/dbus.c +++ b/dlls/winebth.sys/dbus.c @@ -29,6 +29,7 @@ #include <dlfcn.h> #include <assert.h> #include <pthread.h> +#include <errno.h>
#ifdef SONAME_LIBDBUS_1 #include <dbus/dbus.h> @@ -43,6 +44,7 @@ #include <bluetoothapis.h>
#include <wine/debug.h> +#include <wine/rbtree.h>
#include "winebluetooth.h"
@@ -87,7 +89,19 @@ const int bluez_timeout = -1;
#define BLUEZ_DEST "org.bluez" #define BLUEZ_INTERFACE_ADAPTER "org.bluez.Adapter1" -#define BLUEZ_INTERFACE_DEVICE "org.bluez.Device1" +#define BLUEZ_INTERFACE_DEVICE "org.bluez.Device1" +#define BLUEZ_INTERFACE_AGENT "org.bluez.Agent1" +#define BLUEZ_INTERFACE_AGENT_MANAGER "org.bluez.AgentManager1" + +#define BLUEZ_AGENT_METHOD_RELEASE "Release" +#define BLUEZ_AGENT_METHOD_REQUEST_PIN_CODE "RequestPinCode" +#define BLUEZ_AGENT_METHOD_DISPLAY_PIN_CODE "DisplayPinCode" +#define BLUEZ_AGENT_METHOD_REQUEST_PASSKEY "RequestPasskey" +#define BLUEZ_AGENT_METHOD_DISPLAY_PASSKEY "DisplayPasskey" +#define BLUEZ_AGENT_METHOD_REQUEST_CONFIRMATION "RequestConfirmation" +#define BLUEZ_AGENT_METHOD_REQUEST_AUTHORIZATION "RequestAuthorization" +#define BLUEZ_AGENT_METHOD_AUTHORIZE_SERVICE "AuthorizeService" +#define BLUEZ_AGENT_METHOD_CANCEL "Cancel"
#define DO_FUNC( f ) typeof( f ) (*p_##f) DBUS_FUNCS; @@ -1745,7 +1759,825 @@ static struct bluez_watcher_event *bluez_watcher_event_queue_ready( struct bluez return NULL; }
-NTSTATUS bluez_dbus_loop( void *c, void *watcher, +enum bluetooth_auth_request_status +{ + /* No authorisation request is active. */ + BTH_AUTH_REQ_STATUS_NONE, + /* We have received an authorisation request, and are waiting for a response from the user. */ + BTH_AUTH_REQ_STATUS_WAITING_FOR_RESPONSE, + /* A passkey keypress event. Requires no response from the user. */ + BTH_AUTH_REQ_STATUS_KEYPRESS_EVENT +}; + +struct bluez_auth_agent_request +{ + enum bluetooth_auth_request_status status; + + DBusConnection *connection; + DBusMessage *message; + DBusPreallocatedSend *prealloc; + + struct unix_name *device_path; + struct unix_name *adapter_path; + BOOL auth_service; + BLUETOOTH_AUTHENTICATION_METHOD auth_method; + union winebluetooth_auth_event_data auth_data; +}; + +/* BlueZ does not have a way to distingish between method calls to the auth agent for incoming + * authentication/pairing requests, and pairing procedures initiated by us (for instance, by calling + * Pair() on a device). To implement BluetoothAuthenticateDevice, we need to first store the + * user-provided authentication data, initiate the auth process, and then return this data once + * BlueZ calls the auth agent. This struct is used to store that data. */ +struct bluez_auth_agent_reply +{ + struct rb_tree entry; + + struct unix_name *device_path; + char passkey[17]; +}; + +struct bluez_auth_agent_ctx +{ + pthread_mutex_t lock; + /* BlueZ only supports a single authentication session at one time. */ + struct bluez_auth_agent_request cur_request; + /* Replies to auth agent methods for authentication processes initiated by us. Entries are + * removed once they have been sent to BlueZ. */ + struct rb_tree auth_replies; +}; + +static int auth_reply_compare( const void *key, const struct rb_entry *entry ) +{ + struct bluez_auth_agent_reply *reply = + RB_ENTRY_VALUE( entry, struct bluez_auth_agent_reply, entry ); + + return strcmp( key, reply->device_path->str ); +} + +UINT_PTR bluez_auth_agent_ctx_init( void ) +{ + struct bluez_auth_agent_ctx *ctx; + + ctx = calloc( 1, sizeof( *ctx ) ); + if (!ctx) + return (UINT_PTR)NULL; + + pthread_mutex_init( &ctx->lock, NULL ); + ctx->cur_request.status = BTH_AUTH_REQ_STATUS_NONE; + rb_init( &ctx->auth_replies, auth_reply_compare ); + return (UINT_PTR)ctx; +} + +/* Requires cur_request_lock to be held. */ +static void bluez_auth_agent_ctx_reset( struct bluez_auth_agent_ctx *ctx, BOOL free_prealloc ) +{ + if (free_prealloc) + p_dbus_connection_free_preallocated_send( ctx->cur_request.connection, + ctx->cur_request.prealloc ); + p_dbus_message_unref( ctx->cur_request.message ); + p_dbus_connection_unref( ctx->cur_request.connection ); + if (ctx->cur_request.device_path) + unix_name_free( ctx->cur_request.device_path ); + ctx->cur_request.status = BTH_AUTH_REQ_STATUS_NONE; +} + +static void bluez_auth_agent_request_pin_code( DBusConnection *connection, DBusMessage *message, + DBusPreallocatedSend *prealloc, + struct bluez_auth_agent_ctx *ctx ) +{ + DBusMessage *reply_error = NULL; + DBusError error; + const char *device_path = NULL; + struct rb_entry *reply_entry; + + TRACE( "(%p, %s, %p, %p)\n", connection, dbgstr_dbus_message( message ), prealloc, ctx ); + + pthread_mutex_lock( &ctx->lock ); + if (ctx->cur_request.status != BTH_AUTH_REQ_STATUS_NONE) + { + /* This shouldn't happen, as BlueZ only seems to have one auth session active at a time. + * Still, doesn't hurt to check. */ + reply_error = p_dbus_message_new_error_printf( + message, DBUS_ERROR_FAILED, "an authorisation request is already pending" ); + goto error; + } + + p_dbus_error_init( &error ); + if(!p_dbus_message_get_args( message, &error, DBUS_TYPE_OBJECT_PATH, &device_path, + DBUS_TYPE_INVALID )) + { + ERR( "Could not get device parameter from RequestPinCode call: %s: %s.\n", + debugstr_a( error.name ), debugstr_a( error.message ) ); + reply_error = p_dbus_message_new_error( message, error.name, error.message ); + p_dbus_error_free( &error ); + goto error; + } + p_dbus_error_free( &error ); + + reply_entry = rb_get( &ctx->auth_replies, device_path ); + if (reply_entry) + { + /* This auth procedure has been initiated by us, and we have a reply. */ + DBusMessage *reply; + struct bluez_auth_agent_reply *auth_reply = + RB_ENTRY_VALUE( reply_entry, struct bluez_auth_agent_reply, entry ); + + rb_remove( &ctx->auth_replies, reply_entry ); + unix_name_free( auth_reply->device_path ); + reply = p_dbus_message_new_method_return( message ); + if (!reply) + { + free( auth_reply ); + goto error; + } + p_dbus_message_append_args( reply, DBUS_TYPE_STRING, &auth_reply->passkey, + DBUS_TYPE_INVALID ); + free( auth_reply ); + p_dbus_connection_send_preallocated( connection, prealloc, reply, NULL ); + p_dbus_message_unref( reply ); + } + else + { + struct unix_name *device, *adapter; + device = unix_name_get_or_create( device_path ); + if (!device) + goto error; + adapter = bluez_device_path_get_adapter( device_path ); + if (!adapter) + { + unix_name_free( device ); + goto error; + } + + ctx->cur_request.status = BTH_AUTH_REQ_STATUS_WAITING_FOR_RESPONSE; + ctx->cur_request.connection = p_dbus_connection_ref( connection ); + ctx->cur_request.message = p_dbus_message_ref( message ); + ctx->cur_request.prealloc = prealloc; + ctx->cur_request.auth_method = BLUETOOTH_AUTHENTICATION_METHOD_LEGACY; + ctx->cur_request.auth_service = FALSE; + ctx->cur_request.device_path = device; + ctx->cur_request.adapter_path = adapter; + } + + goto done; +error: + if (reply_error) + { + p_dbus_connection_send_preallocated( connection, prealloc, reply_error, NULL ); + p_dbus_message_unref( reply_error ); + } + else p_dbus_connection_free_preallocated_send( connection, prealloc ); +done: + pthread_mutex_unlock( &ctx->lock ); +} + +static void bluez_auth_agent_display_pin_code( DBusConnection *connection, DBusMessage *message, + DBusPreallocatedSend *prealloc, + struct bluez_auth_agent_ctx *ctx ) +{ + DBusMessage *reply_error = NULL; + DBusError error; + const char *device_path = NULL, *pin_code; + struct unix_name *device, *adapter; + ULONG passkey; + + TRACE( "(%p, %s, %p, %p)\n", connection, dbgstr_dbus_message( message ), prealloc, ctx ); + + pthread_mutex_lock( &ctx->lock ); + if (ctx->cur_request.status != BTH_AUTH_REQ_STATUS_NONE) + { + reply_error = p_dbus_message_new_error_printf( + message, DBUS_ERROR_FAILED, "an authorisation request is already pending" ); + goto error; + } + + p_dbus_error_init( &error ); + if (!p_dbus_message_get_args( message, &error, DBUS_TYPE_OBJECT_PATH, + &device_path, DBUS_TYPE_STRING, &pin_code, + DBUS_TYPE_INVALID )) + { + reply_error = p_dbus_message_new_error( message, error.name, error.message ); + goto error; + } + p_dbus_error_free( &error ); + + errno = 0; + passkey = strtol( pin_code, NULL, 10 ); + if (errno) + { + reply_error = + p_dbus_message_new_error( message, "org.bluez.Error.Rejected", "invalid pin code" ); + goto error; + } + + device = unix_name_get_or_create( device_path ); + if (!device) + goto error; + adapter = bluez_device_path_get_adapter( device_path ); + if (!adapter) + { + unix_name_free( device ); + goto error; + } + + ctx->cur_request.status = BTH_AUTH_REQ_STATUS_WAITING_FOR_RESPONSE; + ctx->cur_request.connection = p_dbus_connection_ref( connection ); + ctx->cur_request.message = p_dbus_message_ref( message ); + ctx->cur_request.prealloc = prealloc; + ctx->cur_request.auth_method = BLUETOOTH_AUTHENTICATION_METHOD_PASSKEY_NOTIFICATION; + ctx->cur_request.auth_service = FALSE; + ctx->cur_request.device_path = device; + ctx->cur_request.adapter_path = adapter; + ctx->cur_request.auth_data.passkey = passkey; + + goto done; + error: + if (reply_error) + { + p_dbus_connection_send_preallocated( connection, prealloc, reply_error, NULL ); + p_dbus_message_unref( reply_error ); + } + else p_dbus_connection_free_preallocated_send( connection, prealloc ); +done: + pthread_mutex_unlock( &ctx->lock ); +} + +static void bluez_auth_agent_display_passkey( DBusConnection *connection, DBusMessage *message, + DBusPreallocatedSend *prealloc, + struct bluez_auth_agent_ctx *ctx ) +{ + DBusMessage *reply, *reply_error = NULL; + DBusError error; + const char *device_path = NULL; + struct unix_name *device, *adapter; + dbus_uint32_t passkey; + dbus_uint16_t entered; + + TRACE( "(%p, %s, %p, %p)\n", connection, dbgstr_dbus_message( message ), prealloc, ctx ); + + pthread_mutex_lock( &ctx->lock ); + if (ctx->cur_request.status != BTH_AUTH_REQ_STATUS_NONE) + { + reply_error = p_dbus_message_new_error_printf( + message, DBUS_ERROR_FAILED, "an authorisation request is already pending" ); + goto error; + } + + p_dbus_error_init( &error ); + if (!p_dbus_message_get_args( message, &error, DBUS_TYPE_OBJECT_PATH, + &device_path, DBUS_TYPE_UINT32, &passkey, + DBUS_TYPE_UINT16, &entered, DBUS_TYPE_INVALID )) + { + reply_error = p_dbus_message_new_error( message, error.name, error.message ); + goto error; + } + p_dbus_error_free( &error ); + + device = unix_name_get_or_create( device_path ); + if (!device) + goto error; + adapter = bluez_device_path_get_adapter( device_path ); + if (!adapter) + { + unix_name_free( device ); + goto error; + } + + ctx->cur_request.status = BTH_AUTH_REQ_STATUS_KEYPRESS_EVENT; + ctx->cur_request.device_path = device; + ctx->cur_request.adapter_path = adapter; + ctx->cur_request.auth_data.keypress.passkey = passkey; + ctx->cur_request.auth_data.keypress.entered = entered; + reply = p_dbus_message_new_method_return( message ); + if (!reply) + goto error; + p_dbus_connection_send_preallocated( connection, prealloc, reply, NULL ); + goto done; + + error: + if (reply_error) + { + p_dbus_connection_send_preallocated( connection, prealloc, reply_error, NULL ); + p_dbus_message_unref( reply_error ); + } + else p_dbus_connection_free_preallocated_send( connection, prealloc ); +done: + pthread_mutex_unlock( &ctx->lock ); +} + +static void bluez_auth_agent_request_passkey( DBusConnection *connection, DBusMessage *message, + DBusPreallocatedSend *prealloc, + struct bluez_auth_agent_ctx *ctx ) +{ + DBusMessage *reply_error = NULL; + DBusError error; + const char *device_path = NULL; + struct unix_name *device, *adapter; + struct rb_entry *reply_entry; + + TRACE( "(%p, %s, %p, %p)\n", connection, dbgstr_dbus_message( message ), prealloc, ctx ); + + pthread_mutex_lock( &ctx->lock ); + if (ctx->cur_request.status != BTH_AUTH_REQ_STATUS_NONE) + { + reply_error = p_dbus_message_new_error_printf( + message, DBUS_ERROR_FAILED, "an authorisation request is already pending" ); + goto error; + } + + p_dbus_error_init( &error ); + if (!p_dbus_message_get_args( message, &error, DBUS_TYPE_OBJECT_PATH, &device_path, + DBUS_TYPE_INVALID )) + { + reply_error = p_dbus_message_new_error( message, error.name, error.message ); + p_dbus_error_free( &error ); + goto error; + } + p_dbus_error_free( &error ); + + reply_entry = rb_get( &ctx->auth_replies, device_path ); + if (reply_entry) + { + DBusMessage *reply; + struct bluez_auth_agent_reply *auth_reply = + RB_ENTRY_VALUE( reply_entry, struct bluez_auth_agent_reply, entry ); + dbus_uint32_t passkey; + + rb_remove( &ctx->auth_replies, reply_entry ); + unix_name_free( auth_reply->device_path ); + reply = p_dbus_message_new_method_return( message ); + if (!reply) + { + free( auth_reply ); + goto error; + } + + errno = 0; + passkey = strtol( auth_reply->passkey, NULL, 10 ); + if (errno) + { + /* The user has provided a non-numerical passkey, which wouldn't work. */ + reply_error = p_dbus_message_new_error( message, "org.bluez.Error.Rejected", "" ); + free( auth_reply ); + goto error; + } + p_dbus_message_append_args( message, DBUS_TYPE_UINT32, &passkey, DBUS_TYPE_INVALID ); + free( auth_reply ); + p_dbus_connection_send_preallocated( connection, prealloc, reply, NULL ); + p_dbus_message_unref( reply ); + } + else + { + device = unix_name_get_or_create( device_path ); + if (!device) + goto error; + adapter = bluez_device_path_get_adapter( device_path ); + if (!adapter) + { + unix_name_free( device ); + goto error; + } + + ctx->cur_request.status = BTH_AUTH_REQ_STATUS_WAITING_FOR_RESPONSE; + ctx->cur_request.connection = p_dbus_connection_ref( connection ); + ctx->cur_request.message = p_dbus_message_ref( message ); + ctx->cur_request.prealloc = prealloc; + ctx->cur_request.auth_method = BLUETOOTH_AUTHENTICATION_METHOD_PASSKEY; + ctx->cur_request.auth_service = FALSE; + ctx->cur_request.device_path = device; + ctx->cur_request.adapter_path = adapter; + } + + goto done; + +error: + if (reply_error) + { + p_dbus_connection_send_preallocated( connection, prealloc, reply_error, NULL ); + p_dbus_message_unref( reply_error ); + } + else p_dbus_connection_free_preallocated_send( connection, prealloc ); +done: + pthread_mutex_unlock( &ctx->lock ); +} + +static void bluez_auth_agent_request_confirmation( DBusConnection *connection, DBusMessage *message, + DBusPreallocatedSend *prealloc, + struct bluez_auth_agent_ctx *ctx ) +{ + DBusMessage *reply_error = NULL; + DBusError error; + const char *device_path = NULL; + dbus_uint32_t num; + struct unix_name *device, *adapter; + + TRACE( "(%p, %s, %p, %p)\n", connection, dbgstr_dbus_message( message ), prealloc, ctx ); + + pthread_mutex_lock( &ctx->lock ); + if (ctx->cur_request.status != BTH_AUTH_REQ_STATUS_NONE) + { + reply_error = p_dbus_message_new_error_printf( + message, DBUS_ERROR_FAILED, "an authorisation request is already pending" ); + goto error; + } + + p_dbus_error_init( &error ); + if (!p_dbus_message_get_args( message, &error, DBUS_TYPE_OBJECT_PATH, &device_path, + DBUS_TYPE_UINT32, &num, DBUS_TYPE_INVALID )) + { + reply_error = p_dbus_message_new_error( message, error.name, error.message ); + p_dbus_error_free( &error ); + goto error; + } + p_dbus_error_free( &error ); + + TRACE( "Received a numerical confirmation request from BlueZ for the device %s.\n", + debugstr_a( device_path ) ); + device = unix_name_get_or_create( device_path ); + if (!device) + goto error; + adapter = bluez_device_path_get_adapter( device_path ); + if (!adapter) + { + unix_name_free( device ); + goto error; + } + + ctx->cur_request.status = BTH_AUTH_REQ_STATUS_WAITING_FOR_RESPONSE; + ctx->cur_request.connection = p_dbus_connection_ref( connection ); + ctx->cur_request.message = p_dbus_message_ref( message ); + ctx->cur_request.prealloc = prealloc; + ctx->cur_request.auth_method = BLUETOOTH_AUTHENTICATION_METHOD_NUMERIC_COMPARISON; + ctx->cur_request.auth_data.passkey = num; + ctx->cur_request.auth_service = FALSE; + ctx->cur_request.device_path = device; + ctx->cur_request.adapter_path = adapter; + goto done; + +error: + if (reply_error) + { + p_dbus_connection_send_preallocated( connection, prealloc, reply_error, NULL ); + p_dbus_message_unref( reply_error ); + } + else p_dbus_connection_free_preallocated_send( connection, prealloc ); +done: + pthread_mutex_unlock( &ctx->lock ); +} + +/* BlueZ uses this for Just-Works pairing. In Win32, the equivalent seems to be Numeric + * Comparison with MITMProtectionNotRequired. */ +static void bluez_auth_agent_request_authorization( DBusConnection *connection, + DBusMessage *message, + DBusPreallocatedSend *prealloc, + struct bluez_auth_agent_ctx *ctx ) +{ + DBusMessage *reply_error = NULL; + DBusError error; + const char *device_path = NULL; + struct unix_name *device, *adapter; + + TRACE( "(%p, %s, %p, %p)\n", connection, dbgstr_dbus_message( message ), prealloc, ctx ); + + pthread_mutex_lock( &ctx->lock ); + if (ctx->cur_request.status != BTH_AUTH_REQ_STATUS_NONE) + { + reply_error = p_dbus_message_new_error_printf( + message, DBUS_ERROR_FAILED, "an authorisation request is already pending" ); + goto error; + } + + p_dbus_error_init( &error ); + if (!p_dbus_message_get_args( message, &error, DBUS_TYPE_OBJECT_PATH, &device_path, + DBUS_TYPE_INVALID )) + { + reply_error = p_dbus_message_new_error( message, error.name, error.message ); + p_dbus_error_free( &error ); + goto error; + } + p_dbus_error_free( &error ); + + device = unix_name_get_or_create( device_path ); + if (!device) + goto error; + adapter = bluez_device_path_get_adapter( device_path ); + if (!adapter) + { + unix_name_free( device ); + goto error; + } + + ctx->cur_request.status = BTH_AUTH_REQ_STATUS_WAITING_FOR_RESPONSE; + ctx->cur_request.connection = p_dbus_connection_ref( connection ); + ctx->cur_request.message = p_dbus_message_ref( message ); + ctx->cur_request.prealloc = prealloc; + ctx->cur_request.auth_method = BLUETOOTH_AUTHENTICATION_METHOD_NUMERIC_COMPARISON; + /* Set the passkey to 0 for Just-Works pairing. */ + ctx->cur_request.auth_data.passkey = 0; + ctx->cur_request.auth_service = FALSE; + ctx->cur_request.device_path = device; + ctx->cur_request.adapter_path = adapter; + goto done; + +error: + if (reply_error) + { + p_dbus_connection_send_preallocated( connection, prealloc, reply_error, NULL ); + p_dbus_message_unref( reply_error ); + } + else + p_dbus_connection_free_preallocated_send( connection, prealloc ); + done: + pthread_mutex_unlock( &ctx->lock ); +} + +static void bluez_auth_agent_authorize_service( DBusConnection *connection, DBusMessage *message, + DBusPreallocatedSend *prealloc, + struct bluez_auth_agent_ctx *ctx ) + +{ + DBusMessage *reply_error = NULL; + DBusError error; + GUID guid; + const char *device_path = NULL, *uuid; + struct unix_name *device; + + TRACE( "(%p, %s, %p, %p)\n", connection, dbgstr_dbus_message( message ), prealloc, ctx ); + + pthread_mutex_lock( &ctx->lock ); + if (ctx->cur_request.status != BTH_AUTH_REQ_STATUS_NONE) + { + reply_error = p_dbus_message_new_error_printf( + message, DBUS_ERROR_FAILED, "an authorisation request is already pending" ); + goto error; + } + + p_dbus_error_init( &error ); + if (!p_dbus_message_get_args( message, &error, DBUS_TYPE_OBJECT_PATH, &device_path, + DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID )) + { + reply_error = p_dbus_message_new_error( message, error.name, error.message ); + p_dbus_error_free( &error ); + goto error; + } + p_dbus_error_free( &error ); + + if (!parse_uuid( &guid, uuid )) + { + reply_error = + p_dbus_message_new_error( message, "org.bluez.Error.Rejected", "invalid service uuid" ); + goto error; + } + + TRACE( "Received a service authorisation request from BlueZ for device %s uuid %s.\n", + debugstr_a( device_path ), uuid ); + device = unix_name_get_or_create( device_path ); + if (!device) + goto error; + + ctx->cur_request.status = BTH_AUTH_REQ_STATUS_WAITING_FOR_RESPONSE; + ctx->cur_request.connection = p_dbus_connection_ref( connection ); + ctx->cur_request.message = p_dbus_message_ref( message ); + ctx->cur_request.prealloc = prealloc; + ctx->cur_request.auth_service = TRUE; + ctx->cur_request.device_path = device; + ctx->cur_request.auth_data.service_uuid = guid; + goto done; + +error: + if (reply_error) + { + p_dbus_connection_send_preallocated( connection, prealloc, reply_error, NULL ); + p_dbus_message_unref( reply_error ); + } + else + p_dbus_connection_free_preallocated_send( connection, prealloc ); +done: + pthread_mutex_lock( &ctx->lock ); +} + +static void bluez_auth_agent_cancel( DBusConnection *connection, DBusMessage *message, + DBusPreallocatedSend *prealloc, + struct bluez_auth_agent_ctx *ctx ) +{ + DBusMessage *reply; + TRACE( "(%p, %s, %p, %p)\n", connection, dbgstr_dbus_message( message ), prealloc, ctx ); + + pthread_mutex_lock( &ctx->lock ); + if (ctx->cur_request.status == BTH_AUTH_REQ_STATUS_WAITING_FOR_RESPONSE) + bluez_auth_agent_ctx_reset( ctx, TRUE ); + else + { + ERR("Cancel called without a pending authorisation request\n"); + } + ctx->cur_request.status = BTH_AUTH_REQ_STATUS_NONE; + pthread_mutex_unlock( &ctx->lock ); + + reply = p_dbus_message_new_method_return( message ); + if (reply) + { + p_dbus_connection_send_preallocated( connection, prealloc, reply, NULL ); + } + else p_dbus_connection_free_preallocated_send( connection, prealloc ); +} + +static void bluez_auth_agent_release( DBusConnection *connection, DBusMessage *message, + DBusPreallocatedSend *prealloc, + struct bluez_auth_agent_ctx *ctx ) +{ + DBusMessage *reply; + TRACE( "(%p, %s, %p, %p)\n", connection, dbgstr_dbus_message( message ), prealloc, ctx ); + + pthread_mutex_lock( &ctx->lock ); + if (ctx->cur_request.status == BTH_AUTH_REQ_STATUS_WAITING_FOR_RESPONSE) + bluez_auth_agent_ctx_reset( ctx, TRUE ); + pthread_mutex_unlock( &ctx->lock ); + + reply = p_dbus_message_new_method_return( message ); + if (reply == NULL) + { + p_dbus_connection_free_preallocated_send( connection, prealloc ); + return; + } + p_dbus_connection_send_preallocated( connection, prealloc, reply, NULL ); +} + +struct dbus_object_method_entry +{ + const char *interface; + const char *method_name; + void (*method_callback)( DBusConnection *, DBusMessage *, DBusPreallocatedSend *, + struct bluez_auth_agent_ctx * ); +}; + +const static struct dbus_object_method_entry + bluez_auth_agent_object_methods[] = { + {BLUEZ_INTERFACE_AGENT, BLUEZ_AGENT_METHOD_RELEASE, bluez_auth_agent_release}, + {BLUEZ_INTERFACE_AGENT, BLUEZ_AGENT_METHOD_REQUEST_PIN_CODE, bluez_auth_agent_request_pin_code}, + {BLUEZ_INTERFACE_AGENT, BLUEZ_AGENT_METHOD_DISPLAY_PIN_CODE, bluez_auth_agent_display_pin_code}, + {BLUEZ_INTERFACE_AGENT, BLUEZ_AGENT_METHOD_REQUEST_PASSKEY, bluez_auth_agent_request_passkey}, + {BLUEZ_INTERFACE_AGENT, BLUEZ_AGENT_METHOD_DISPLAY_PASSKEY, bluez_auth_agent_display_passkey}, + {BLUEZ_INTERFACE_AGENT, BLUEZ_AGENT_METHOD_REQUEST_CONFIRMATION, bluez_auth_agent_request_confirmation}, + {BLUEZ_INTERFACE_AGENT, BLUEZ_AGENT_METHOD_REQUEST_AUTHORIZATION, bluez_auth_agent_request_authorization}, + {BLUEZ_INTERFACE_AGENT, BLUEZ_AGENT_METHOD_AUTHORIZE_SERVICE, bluez_auth_agent_authorize_service}, + {BLUEZ_INTERFACE_AGENT, BLUEZ_AGENT_METHOD_CANCEL, bluez_auth_agent_cancel} +}; + +static DBusHandlerResult bluez_auth_agent_vtable_message_handler( DBusConnection *connection, + DBusMessage *message, void *data ) +{ + struct bluez_auth_agent_ctx *ctx = data; + SIZE_T i; + + TRACE_(dbus)("(%p, %s, %p)", connection, dbgstr_dbus_message( message ), data); + + for (i = 0; i < ARRAYSIZE( bluez_auth_agent_object_methods ); i++) + { + const char *iface = bluez_auth_agent_object_methods[i].interface; + const char *name = bluez_auth_agent_object_methods[i].method_name; + + if (p_dbus_message_is_method_call( message, iface, name )) + { + DBusPreallocatedSend *prealloc = p_dbus_connection_preallocate_send( connection ); + if (!prealloc) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + bluez_auth_agent_object_methods[i].method_callback( connection, message, prealloc, + ctx ); + return DBUS_HANDLER_RESULT_HANDLED; + } + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +const static struct DBusObjectPathVTable bluez_auth_agent_object_vtable = { + .unregister_function = NULL, .message_function = bluez_auth_agent_vtable_message_handler }; + +#define WINE_BLUEZ_AUTH_AGENT_PATH "/org/winehq/wine/winebth/AuthAgent" + +NTSTATUS bluez_auth_agent_register( void *connection, void *agent_ctx, + BLUETOOTH_IO_CAPABILITY capabilities ) +{ + DBusMessage *request, *reply; + DBusError error; + dbus_bool_t success; + NTSTATUS result; + const char *capability; + struct bluez_auth_agent_ctx *ctx = agent_ctx; + static const char *wine_bluez_auth_agent_path = WINE_BLUEZ_AUTH_AGENT_PATH; + + TRACE( "(%p, %p, %d)\n", connection, ctx, capabilities ); + + switch (capabilities) + { + case BLUETOOTH_IO_CAPABILITY_KEYBOARDONLY: + capability = "KeyboardOnly"; + break; + case BLUETOOTH_IO_CAPABILITY_DISPLAYONLY: + capability = "DisplayOnly"; + break; + case BLUETOOTH_IO_CAPABILITY_DISPLAYYESNO: + capability = "DisplayYesNo"; + case BLUETOOTH_IO_CAPABILITY_NOINPUTNOOUTPUT: + capability = "NoInputNoOutput"; + break; + case BLUETOOTH_IO_CAPABILITY_UNDEFINED: + capability = ""; + break; + default: + return STATUS_INVALID_PARAMETER; + } + p_dbus_error_init( &error ); + + TRACE_(dbus)( "Registering an org.bluez.Agent1 object at %s\n", WINE_BLUEZ_AUTH_AGENT_PATH ); + success = p_dbus_connection_try_register_object_path( + connection, WINE_BLUEZ_AUTH_AGENT_PATH, &bluez_auth_agent_object_vtable, ctx, + &error ); + if (!success) + { + ERR_(dbus)( "Could not register BlueZ agent: %s: %s\n", + debugstr_a( error.name ), debugstr_a( error.message ) ); + result = bluez_dbus_error_to_ntstatus( &error ); + goto done; + } + + request = p_dbus_message_new_method_call( BLUEZ_DEST, "/org/bluez", + BLUEZ_INTERFACE_AGENT_MANAGER, "RegisterAgent" ); + if (!request) + { + result = STATUS_NO_MEMORY; + goto failure; + } + + p_dbus_message_append_args( request, DBUS_TYPE_OBJECT_PATH, &wine_bluez_auth_agent_path, + DBUS_TYPE_STRING, &capability, DBUS_TYPE_INVALID ); + reply = + p_dbus_connection_send_with_reply_and_block( connection, request, bluez_timeout, &error ); + p_dbus_message_unref( request ); + if (!reply) + { + ERR_(dbus)( "failed to register agent: '%s': %s\n", error.name, error.message ); + result = bluez_dbus_error_to_ntstatus( &error ); + goto failure; + } + p_dbus_message_unref( reply ); + result = STATUS_SUCCESS; + goto done; + +failure: + p_dbus_connection_unregister_object_path( connection, WINE_BLUEZ_AUTH_AGENT_PATH ); + free( ctx ); +done: + p_dbus_error_free( &error ); + return result; +} + +BOOL bluez_auth_agent_unregister( void *connection, void *agent_ctx ) +{ + dbus_bool_t success; + success = p_dbus_connection_unregister_object_path( connection, WINE_BLUEZ_AUTH_AGENT_PATH ); + return success != 0; +} + + +static BOOL bluez_auth_agent_have_auth_event( struct bluez_auth_agent_ctx *ctx, + struct winebluetooth_auth_event *auth_event ) +{ + struct bluez_auth_agent_request *req = &ctx->cur_request; + BOOL have_event; + + pthread_mutex_lock( &ctx->lock ); + switch (ctx->cur_request.status) + { + case BTH_AUTH_REQ_STATUS_WAITING_FOR_RESPONSE: + { + struct bluez_auth_agent_request *req = &ctx->cur_request; + + auth_event->auth_data = req->auth_data; + auth_event->event_type = req->auth_service + ? BLUETOOTH_AUTH_EVENT_DEVICE_SERVICE_AUTH_REQUEST + : BLUETOOTH_AUTH_EVENT_DEVICE_AUTH_REQUEST; + auth_event->auth_method = req->auth_method; + auth_event->device.handle = req->device_path; + have_event = TRUE; + } + case BTH_AUTH_REQ_STATUS_KEYPRESS_EVENT: + { + auth_event->event_type = BLUETOOTH_AUTH_EVENT_DEVICE_KEYPRESS; + auth_event->device.handle = req->device_path; + auth_event->auth_data = req->auth_data; + have_event = TRUE; + } + default: + have_event = FALSE; + } + pthread_mutex_unlock( &ctx->lock ); + + return have_event; +} + +NTSTATUS bluez_dbus_loop( void *c, void *watcher, void *auth_agent_ctx, struct winebluetooth_event_loop_result *result ) { DBusConnection *connection; @@ -1757,6 +2589,7 @@ NTSTATUS bluez_dbus_loop( void *c, void *watcher, while(TRUE) { struct bluez_watcher_event *bluez_event; + struct winebluetooth_auth_event auth_event = {0};
/* The first two conditionals can probably be nested within a single boolean like * 'init_objects_enumerated', but I have decided to not do that for now to keep this state @@ -1799,6 +2632,13 @@ NTSTATUS bluez_dbus_loop( void *c, void *watcher, p_dbus_connection_unref( connection ); return STATUS_PENDING; } + else if (bluez_auth_agent_have_auth_event( auth_agent_ctx, &auth_event )) + { + result->status = WINEBLUETOOTH_EVENT_LOOP_STATUS_AUTH_EVENT; + result->data.auth_event = auth_event; + p_dbus_connection_unref( connection ); + return STATUS_PENDING; + } else if (!p_dbus_connection_read_write_dispatch( connection, -1 )) { p_dbus_connection_unref( connection ); diff --git a/dlls/winebth.sys/unixlib.c b/dlls/winebth.sys/unixlib.c index 017e073207e..70162631747 100644 --- a/dlls/winebth.sys/unixlib.c +++ b/dlls/winebth.sys/unixlib.c @@ -201,7 +201,8 @@ static NTSTATUS bluetooth_event_loop_once( void *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 ); + return bluez_dbus_loop( dbus_connection, params->unix_watcher_ctx, (void *)params->auth_agent, + ¶ms->result ); }
static NTSTATUS bluetooth_guid_array_copy( void *args ) @@ -219,6 +220,35 @@ static NTSTATUS bluetooth_guid_array_free( void *args ) return STATUS_SUCCESS; }
+static NTSTATUS bluetooth_auth_agent_init( void *args ) +{ + struct bluetooth_auth_agent_init_params *params = args; + + if (!dbus_connection) return STATUS_NOT_SUPPORTED; + + params->auth_agent = bluez_auth_agent_ctx_init(); + return !params->auth_agent ? STATUS_NO_MEMORY : STATUS_SUCCESS; +} + +static NTSTATUS bluetooth_auth_agent_start( void *args ) +{ + struct bluetooth_auth_agent_start_params *params = args; + + if (!dbus_connection) return STATUS_NOT_SUPPORTED; + return bluez_auth_agent_register( dbus_connection, (void *)params->auth_agent, + params->capability ); +} + +static NTSTATUS bluetooth_auth_agent_stop( void *args ) +{ + struct bluetooth_auth_agent_stop_params *params = args; + + if (!dbus_connection) return STATUS_NOT_SUPPORTED; + return bluez_auth_agent_unregister( dbus_connection, (void *)params->auth_agent ) + ? STATUS_SUCCESS + : STATUS_INTERNAL_ERROR; +} + const unixlib_entry_t __wine_unix_call_funcs[] = { bluetooth_init, bluetooth_shutdown, @@ -239,6 +269,10 @@ const unixlib_entry_t __wine_unix_call_funcs[] = {
bluetooth_guid_array_copy, bluetooth_guid_array_free, + + bluetooth_auth_agent_init, + bluetooth_auth_agent_start, + bluetooth_auth_agent_stop, };
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 ba468e54b19..fa5b8e70066 100644 --- a/dlls/winebth.sys/unixlib.h +++ b/dlls/winebth.sys/unixlib.h @@ -118,6 +118,7 @@ struct bluetooth_watcher_close_params struct bluetooth_event_loop_once_params { unix_handle_t unix_watcher_ctx; + unix_handle_t auth_agent;
struct winebluetooth_event_loop_result result; }; @@ -135,6 +136,22 @@ struct bluetooth_guid_array_free_params UINT_PTR arr; };
+struct bluetooth_auth_agent_init_params +{ + UINT_PTR auth_agent; +}; + +struct bluetooth_auth_agent_start_params +{ + UINT_PTR auth_agent; + BLUETOOTH_IO_CAPABILITY capability; +}; + +struct bluetooth_auth_agent_stop_params +{ + UINT_PTR auth_agent; +}; + enum bluetoothapis_funcs { unix_bluetooth_init, @@ -157,6 +174,10 @@ enum bluetoothapis_funcs unix_bluetooth_guid_array_copy, unix_bluetooth_guid_array_free,
+ unix_bluetooth_auth_agent_init, + unix_bluetooth_auth_agent_start, + unix_bluetooth_auth_agent_stop, + unix_funcs_count };
diff --git a/dlls/winebth.sys/unixlib_priv.h b/dlls/winebth.sys/unixlib_priv.h index 2375aee531f..56042a959a9 100644 --- a/dlls/winebth.sys/unixlib_priv.h +++ b/dlls/winebth.sys/unixlib_priv.h @@ -48,7 +48,7 @@ extern void unix_name_free( struct unix_name *name ); extern void *bluez_dbus_init( void ); extern void bluez_dbus_close( void *connection ); extern void bluez_dbus_free( void *connection ); -extern NTSTATUS bluez_dbus_loop( void *connection, void *watcher_ctx, struct winebluetooth_event_loop_result *result ); +extern NTSTATUS bluez_dbus_loop( void *connection, void *watcher_ctx, void *auth_agent_ctx, struct winebluetooth_event_loop_result *result ); extern NTSTATUS bluez_adapter_set_prop( void *connection, struct bluetooth_adapter_set_prop_params *params ); extern NTSTATUS bluez_adapter_start_discovery( void *connection, const char *adapter_path, @@ -56,5 +56,9 @@ extern NTSTATUS bluez_adapter_start_discovery( void *connection, const char *ada extern NTSTATUS bluez_adapter_stop_discovery( void *connection, const char *adapter_path ); extern NTSTATUS bluez_watcher_init( void *connection, void **ctx ); extern void bluez_watcher_close( void *connection, void *ctx ); +extern NTSTATUS bluez_auth_agent_register( void *connection, void *ctx, + BLUETOOTH_IO_CAPABILITY capabilities ); +extern BOOL bluez_auth_agent_unregister( void *connection, void *agent_ctx ); +extern UINT_PTR bluez_auth_agent_ctx_init( void ); #endif /* SONAME_LIBDBUS_1 */ #endif /* __WINE_BLUETOOTHAPIS_UNIXLIB_PRIV_H */ diff --git a/dlls/winebth.sys/winebluetooth.c b/dlls/winebth.sys/winebluetooth.c index 525db1fa2e1..98f0afb9269 100644 --- a/dlls/winebth.sys/winebluetooth.c +++ b/dlls/winebth.sys/winebluetooth.c @@ -162,7 +162,8 @@ NTSTATUS winebluetooth_watcher_close( winebluetooth_watcher_t watcher ) }
NTSTATUS winebluetooth_event_loop_run( winebluetooth_watcher_t watcher, - struct winebluetooth_event_loop_result *result ) + winebluetooth_auth_agent_t agent, + struct winebluetooth_event_loop_result *result ) { struct bluetooth_event_loop_once_params params = {0}; NTSTATUS status; @@ -170,11 +171,52 @@ NTSTATUS winebluetooth_event_loop_run( winebluetooth_watcher_t watcher, TRACE( "(%p, %p, %p)\n", watcher, result );
params.unix_watcher_ctx = watcher.handle; + params.auth_agent = agent.handle; status = UNIX_BLUETOOTH_CALL( bluetooth_event_loop_once, ¶ms ); *result = params.result; return status; }
+NTSTATUS winebluetooth_auth_agent_init( winebluetooth_auth_agent_t *agent ) +{ + NTSTATUS status; + struct bluetooth_auth_agent_init_params params = {0}; + + TRACE( "(%p)\n", agent ); + + status = UNIX_BLUETOOTH_CALL( bluetooth_auth_agent_init, ¶ms ); + if (status) + return status; + + agent->handle = params.auth_agent; + return STATUS_SUCCESS; +} + +NTSTATUS winebluetooth_auth_agent_start( winebluetooth_auth_agent_t agent, BLUETOOTH_IO_CAPABILITY capability ) +{ + struct bluetooth_auth_agent_start_params params = {0}; + NTSTATUS status; + + TRACE( "(%p, %d)\n", agent.handle, capability ); + + params.auth_agent = agent.handle; + params.capability = capability; + status = UNIX_BLUETOOTH_CALL( bluetooth_auth_agent_start, ¶ms ); + return status; +} + +NTSTATUS winebluetooth_auth_agent_stop( winebluetooth_auth_agent_t agent ) +{ + struct bluetooth_auth_agent_stop_params params = {0}; + NTSTATUS status; + + TRACE( "(%p)\n", agent.handle ); + + params.auth_agent = agent.handle; + status = UNIX_BLUETOOTH_CALL( bluetooth_auth_agent_stop, ¶ms ); + return status; +} + NTSTATUS winebluetooth_init( void ) { NTSTATUS status; diff --git a/dlls/winebth.sys/winebluetooth.h b/dlls/winebth.sys/winebluetooth.h index f3cf220ae31..5efb3527ad0 100644 --- a/dlls/winebth.sys/winebluetooth.h +++ b/dlls/winebth.sys/winebluetooth.h @@ -270,9 +270,47 @@ struct winebluetooth_watcher_event NTSTATUS winebluetooth_watcher_init( winebluetooth_watcher_t *watcher ); NTSTATUS winebluetooth_watcher_close( winebluetooth_watcher_t watcher );
+typedef struct +{ + UINT_PTR handle; +} winebluetooth_auth_agent_t; + +NTSTATUS winebluetooth_auth_agent_init( winebluetooth_auth_agent_t *agent ); +NTSTATUS winebluetooth_auth_agent_start( winebluetooth_auth_agent_t agent, + BLUETOOTH_IO_CAPABILITY capability ); +NTSTATUS winebluetooth_auth_agent_stop( winebluetooth_auth_agent_t agent ); + +union winebluetooth_auth_event_data +{ + UCHAR pincode[BLUETOOTH_MAX_PASSKEY_BUFFER_SIZE]; + ULONG passkey; + GUID service_uuid; + struct { + ULONG passkey; + UINT16 entered; + } keypress; +}; + +enum winebluetooth_auth_event_type +{ + BLUETOOTH_AUTH_EVENT_DEVICE_AUTH_REQUEST, + BLUETOOTH_AUTH_EVENT_DEVICE_SERVICE_AUTH_REQUEST, + BLUETOOTH_AUTH_EVENT_DEVICE_KEYPRESS, +}; + +struct winebluetooth_auth_event +{ + enum winebluetooth_auth_event_type event_type; + winebluetooth_device_t device; + winebluetooth_radio_t radio; + BLUETOOTH_AUTHENTICATION_METHOD auth_method; + union winebluetooth_auth_event_data auth_data; +}; + enum winebluetooth_event_loop_status { WINEBLUETOOTH_EVENT_LOOP_STATUS_WATCHER_EVENT, + WINEBLUETOOTH_EVENT_LOOP_STATUS_AUTH_EVENT, };
struct winebluetooth_event_loop_result @@ -280,10 +318,12 @@ struct winebluetooth_event_loop_result enum winebluetooth_event_loop_status status; union { struct winebluetooth_watcher_event watcher_event; + struct winebluetooth_auth_event auth_event; } data; };
NTSTATUS winebluetooth_event_loop_run( winebluetooth_watcher_t watcher, + winebluetooth_auth_agent_t auth_agent, struct winebluetooth_event_loop_result *result ); NTSTATUS winebluetooth_init( void ); NTSTATUS winebluetooth_shutdown( void ); diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index 1f7adfb4eb0..59256369870 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -67,6 +67,7 @@ DECLARE_CRITICAL_SECTION( device_list_cs ); static struct list device_list = LIST_INIT( device_list );
static winebluetooth_watcher_t watcher; +static winebluetooth_auth_agent_t auth_agent; struct bluetooth_radio { struct list entry; @@ -81,6 +82,7 @@ struct bluetooth_radio UNICODE_STRING bthport_symlink_name; UNICODE_STRING bthradio_symlink_name; LIST_ENTRY irp_list; + BOOL auth_agent_registered; };
static NTSTATUS WINAPI dispatch_bluetooth(DEVICE_OBJECT *device, IRP *irp ) @@ -213,10 +215,37 @@ static NTSTATUS WINAPI dispatch_bluetooth(DEVICE_OBJECT *device, IRP *irp ) } case IOCTL_WINEBTH_RADIO_STOP_DISCOVERY: status = winebluetooth_radio_stop_discovery( ext->radio ); - default: break; - } + case IOCTL_WINEBTH_START_AUTH_AGENT: + { + BLUETOOTH_IO_CAPABILITY *capability = + (BLUETOOTH_IO_CAPABILITY *)irp->AssociatedIrp.SystemBuffer; + if (!capability) + { + status = STATUS_INVALID_PARAMETER; + break; + } + if (insize < sizeof(*capability)) + { + status = STATUS_BUFFER_TOO_SMALL; + break; + }
+ status = ext->auth_agent_registered + ? STATUS_SUCCESS + : winebluetooth_auth_agent_start( auth_agent, *capability ); + ext->auth_agent_registered = TRUE; + break; + } + case IOCTL_WINEBTH_STOP_AUTH_AGENT: + { + status = winebluetooth_auth_agent_stop( auth_agent ); + ext->auth_agent_registered = !status ? FALSE : TRUE; + break; + } + default: + break; + }
irp->IoStatus.Status = status; IoCompleteRequest( irp, IO_NO_INCREMENT ); @@ -460,7 +489,7 @@ static DWORD CALLBACK bluetooth_event_loop_thread_proc( void *arg ) { struct winebluetooth_event_loop_result result = {0};
- status = winebluetooth_event_loop_run( watcher, &result ); + status = winebluetooth_event_loop_run( watcher, auth_agent, &result ); if (status != STATUS_PENDING) break;
switch (result.status) @@ -494,6 +523,21 @@ static DWORD CALLBACK bluetooth_event_loop_thread_proc( void *arg ) } break; } + case WINEBLUETOOTH_EVENT_LOOP_STATUS_AUTH_EVENT: + { + struct bluetooth_radio *device; + EnterCriticalSection( &device_list_cs ); + LIST_FOR_EACH_ENTRY( device, &device_list, struct bluetooth_radio, entry ) + { + if (winebluetooth_radio_equal( device->radio, result.data.auth_event.radio )) + { + bthenum_device_auth_event( device->device_fdo_bus, result.data.auth_event ); + winebluetooth_radio_free( result.data.auth_event.radio ); + break; + } + } + LeaveCriticalSection( &device_list_cs ); + } default: FIXME( "Unknown bluetooth event loop status code: %#x\n", result.status ); } @@ -840,6 +884,12 @@ NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) ERR( "Failed to initialize bluetooth watcher: %#lx\n", status ); return status; } + status = winebluetooth_auth_agent_init( &auth_agent ); + if (status) + { + ERR( "Failed to initialize bluetooth authentication agent: %#lx\n", status ); + return status; + }
driver_obj = driver;
diff --git a/dlls/winebth.sys/winebth_priv.h b/dlls/winebth.sys/winebth_priv.h index 32d0d2ad95b..9fe425846e6 100644 --- a/dlls/winebth.sys/winebth_priv.h +++ b/dlls/winebth.sys/winebth_priv.h @@ -40,6 +40,8 @@ extern NTSTATUS WINAPI bthenum_device_removed( DEVICE_OBJECT *fdo_bus, const winebluetooth_device_t device_handle ); extern NTSTATUS WINAPI bthenum_device_props_changed( DEVICE_OBJECT *fdo_bus, struct winebluetooth_watcher_event_device_props_changed event ); +extern NTSTATUS WINAPI bthenum_device_auth_event( DEVICE_OBJECT *fdo_bus, + struct winebluetooth_auth_event event ); extern NTSTATUS WINAPI bthenum_create_fdo_bus( DEVICE_OBJECT *pdo, DEVICE_OBJECT **fdo_bus, UINT16 localmfg ); extern NTSTATUS WINAPI bthenum_entry_point( DRIVER_OBJECT *driver, UNICODE_STRING *path ); diff --git a/include/bthdef.h b/include/bthdef.h index 6c200073995..5b89834a711 100644 --- a/include/bthdef.h +++ b/include/bthdef.h @@ -51,6 +51,12 @@ DEFINE_GUID( GUID_BTHPORT_DEVICE_INTERFACE, 0x850302a, 0xb344, 0x4fda, 0x9b, 0xe
DEFINE_GUID( GUID_BLUETOOTH_RADIO_IN_RANGE, 0xea3b5b82, 0x26ee, 0x450e, 0xb0, 0xd8, 0xd2, 0x6f, 0xe3, 0x0a, 0x38, 0x69 ); + +DEFINE_GUID( GUID_BLUETOOTH_AUTHENTICATION_REQUEST, 0x5dc9136d, 0x996c, 0x46db, 0x84, 0xf5, 0x32, + 0xc0, 0xa3, 0xf4, 0x73, 0x52 ); + +DEFINE_GUID( GUID_BLUETOOTH_KEYPRESS_EVENT, 0xd668dfcd, 0x0f4e, 0x4efc, 0xbf, 0xe0, 0x39, 0x2e, + 0xee, 0xc5, 0x10, 0x9c ); typedef struct _BTH_RADIO_IN_RANGE { BTH_DEVICE_INFO deviceInfo; @@ -58,6 +64,41 @@ typedef struct _BTH_RADIO_IN_RANGE ULONG previousDeviceFlags; } BTH_RADIO_IN_RANGE, *PBTH_RADIO_IN_RANGE;
+/* Associated data for GUID_BLUETOOTH_AUTHENTICATION_REQUEST events. */ +typedef struct _BTH_AUTHENTICATION_REQUEST +{ + BTH_DEVICE_INFO deviceInfo; + BLUETOOTH_AUTHENTICATION_METHOD authenticationMethod; + BLUETOOTH_IO_CAPABILITY IoCapability; + BLUETOOTH_AUTHENTICATION_REQUIREMENTS AuthenticationRequirements; + + /* Not sure what this field does. */ + ULONG flag; + + union + { + ULONG Numeric_Value; + ULONG Passkey; + }; +} BTH_AUTHENTICATION_REQUEST, *PBTH_AUTHENTICATION_REQUEST; + +typedef enum _BTH_KEYPRESS_NOTIFICATION_TYPE +{ + BTH_KEYPRESS_NOTIFICATION_PASSKEY_STARTED, + BTH_KEYPRESS_NOTIFICATION_PASSKEY_DIGIT_ENTERED, + BTH_KEYPRESS_NOTIFICATION_PASSKEY_DIGIT_ERASED, + BTH_KEYPRESS_NOTIFICATION_PASSKEY_CLEARED, + BTH_KEYPRESS_NOTIFICATION_PASSKEY_ENTRY_COMPLETED, +} BTH_KEYPRESS_NOTIFICATION_TYPE, *PBTH_KEYPRESS_NOTIFICATION_TYPE; + +/* Associated data for GUID_BLUETOOTH_KEYPRESS_EVENT events. */ + +typedef struct _BTH_HCI_KEYPRESS_INFO +{ + BTH_ADDR BTH_ADDR; + BTH_KEYPRESS_NOTIFICATION_TYPE NotificationType; +} BTH_HCI_KEYPRESS_INFO, *PBTH_HCI_KEYPRESS_INFO; + #ifdef __cplusplus } #endif diff --git a/include/bthioctl.h b/include/bthioctl.h index 0b9cc6164a4..f858f30c93e 100644 --- a/include/bthioctl.h +++ b/include/bthioctl.h @@ -46,6 +46,8 @@ #define IOCTL_WINEBTH_RADIO_UNSET_FLAG CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xa4, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_WINEBTH_RADIO_START_DISCOVERY CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xa6, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_WINEBTH_RADIO_STOP_DISCOVERY CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xa7, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_WINEBTH_START_AUTH_AGENT CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xa8, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_WINEBTH_STOP_AUTH_AGENT CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xa9, METHOD_BUFFERED, FILE_ANY_ACCESS) #endif
#define LOCAL_RADIO_DISCOVERABLE (0x0001)
From: Vibhav Pant vibhavp@gmail.com
--- dlls/winebth.sys/bthenum.c | 35 ++++++++++++ dlls/winebth.sys/dbus.c | 93 ++++++++++++++++++++++++++++++++ dlls/winebth.sys/unixlib.c | 10 ++++ dlls/winebth.sys/unixlib.h | 7 +++ dlls/winebth.sys/unixlib_priv.h | 2 + dlls/winebth.sys/winebluetooth.c | 15 ++++++ dlls/winebth.sys/winebluetooth.h | 10 ++++ dlls/winebth.sys/winebth.c | 20 +++++++ include/bthioctl.h | 18 +++++++ 9 files changed, 210 insertions(+)
diff --git a/dlls/winebth.sys/bthenum.c b/dlls/winebth.sys/bthenum.c index b12c8d9ab94..511dde8a189 100644 --- a/dlls/winebth.sys/bthenum.c +++ b/dlls/winebth.sys/bthenum.c @@ -1151,6 +1151,41 @@ static NTSTATUS WINAPI bthenum_ioctl( DEVICE_OBJECT *device, IRP *irp ) status = have_buf ? STATUS_SUCCESS : STATUS_INVALID_BUFFER_SIZE; break; } + case IOCTL_WINEBTH_SEND_AUTH_RESPONSE: + { + struct winebth_authentication_response *response = + (struct winebth_authentication_response *)irp->AssociatedIrp.SystemBuffer; + struct bthenum_phys_remote_radio_ext *remote_device; + BOOL found = FALSE; + if (response == NULL) + { + status = STATUS_INVALID_PARAMETER; + break; + } + if (insize < sizeof(*response)) + { + status = STATUS_INVALID_BUFFER_SIZE; + break; + } + if (response->auth_data.pin.length > 16) + { + status = STATUS_INVALID_PARAMETER; + break; + } + RtlEnterCriticalSection( &fdo->cs ); + LIST_FOR_EACH_ENTRY( remote_device, &fdo->devices, struct bthenum_phys_remote_radio_ext, entry ) + { + if (remote_device->props.address.ullLong == response->address && !remote_device->base.base.removed) + { + IoSkipCurrentIrpStackLocation( irp ); + found = TRUE; + status = IoCallDriver( fdo->parent_radio_pdo, irp ); + break; + } + } + RtlLeaveCriticalSection( &fdo->cs ); + if (!found) status = STATUS_DEVICE_DOES_NOT_EXIST; + } default: IoSkipCurrentIrpStackLocation( irp ); return IoCallDriver( fdo->parent_radio_pdo, irp ); diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index 31251806822..5f10b39fd08 100644 --- a/dlls/winebth.sys/dbus.c +++ b/dlls/winebth.sys/dbus.c @@ -2540,6 +2540,99 @@ BOOL bluez_auth_agent_unregister( void *connection, void *agent_ctx ) return success != 0; }
+NTSTATUS bluez_auth_agent_send_response( void *agent_ctx, + const struct winebluetooth_auth_response *response ) +{ + NTSTATUS ret; + DBusMessage *reply = NULL; + dbus_bool_t success = TRUE; + struct bluez_auth_agent_ctx *ctx = agent_ctx; + + pthread_mutex_lock( &ctx->lock ); + if (ctx->cur_request.status != BTH_AUTH_REQ_STATUS_WAITING_FOR_RESPONSE) + { + ret = STATUS_CANCELLED; + goto done; + } + reply = + response->negative_response + ? p_dbus_message_new_error( ctx->cur_request.message, "org.bluez.Error.Rejected", "" ) + : p_dbus_message_new_method_return( ctx->cur_request.message ); + if (!reply) + { + ret = STATUS_NO_MEMORY; + goto done; + } + if (response->negative_response) + { + p_dbus_connection_send_preallocated( ctx->cur_request.connection, ctx->cur_request.prealloc, + reply, NULL ); + bluez_auth_agent_ctx_reset( ctx, FALSE ); + ret = STATUS_SUCCESS; + goto done; + } + switch (response->auth_method) + { + case BLUETOOTH_AUTHENTICATION_METHOD_LEGACY: + if (ctx->cur_request.auth_method == BLUETOOTH_AUTHENTICATION_METHOD_PASSKEY) + { + dbus_uint32_t passkey; + + errno = 0; + passkey = strtol( (char *)response->auth_data.pincode, NULL, 10 ); + if (errno) + { + /* The user has provided a non-numerical passkey, which wouldn't work. */ + p_dbus_message_unref( reply ); + reply = p_dbus_message_new_error( ctx->cur_request.message, + "org.bluez.Error.Rejected", "" ); + if (!reply) + { + ret = STATUS_CANCELLED; + goto done; + } + } + success = p_dbus_message_append_args( reply, DBUS_TYPE_UINT32, &passkey, + DBUS_TYPE_INVALID ); + } + else + { + const char *pin = (char *)response->auth_data.pincode; + success = + p_dbus_message_append_args( reply, DBUS_TYPE_STRING, &pin, DBUS_TYPE_INVALID ); + } + break; + case BLUETOOTH_AUTHENTICATION_METHOD_PASSKEY: + if (ctx->cur_request.auth_method == BLUETOOTH_AUTHENTICATION_METHOD_LEGACY) + { + char pin[17]; + snprintf( pin, sizeof( pin ), "%d", (int)response->auth_data.passkey ); + success = p_dbus_message_append_args( reply, DBUS_TYPE_STRING, &pin, DBUS_TYPE_INVALID ); + } + else + { + dbus_uint32_t passkey = response->auth_data.passkey; + success = p_dbus_message_append_args( reply, DBUS_TYPE_UINT32, &passkey, DBUS_TYPE_INVALID ); + } + break; + default: + break; + } + if (!success) + { + ret = STATUS_NO_MEMORY; + goto done; + } + + p_dbus_connection_send_preallocated( ctx->cur_request.connection, ctx->cur_request.prealloc, + reply, NULL ); + bluez_auth_agent_ctx_reset( ctx, FALSE ); + ret = STATUS_SUCCESS; +done: + pthread_mutex_unlock( &ctx->lock ); + if (reply) p_dbus_message_unref( reply ); + return ret; +}
static BOOL bluez_auth_agent_have_auth_event( struct bluez_auth_agent_ctx *ctx, struct winebluetooth_auth_event *auth_event ) diff --git a/dlls/winebth.sys/unixlib.c b/dlls/winebth.sys/unixlib.c index 70162631747..f1263e27892 100644 --- a/dlls/winebth.sys/unixlib.c +++ b/dlls/winebth.sys/unixlib.c @@ -249,6 +249,15 @@ static NTSTATUS bluetooth_auth_agent_stop( void *args ) : STATUS_INTERNAL_ERROR; }
+static NTSTATUS bluetooth_auth_agent_send_response( void *args ) +{ + struct bluetooth_auth_agent_send_response_params *params = args; + + if (!dbus_connection) return STATUS_NOT_SUPPORTED; + + return bluez_auth_agent_send_response( (void *)params->agent, params->response ); +} + const unixlib_entry_t __wine_unix_call_funcs[] = { bluetooth_init, bluetooth_shutdown, @@ -273,6 +282,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = { bluetooth_auth_agent_init, bluetooth_auth_agent_start, bluetooth_auth_agent_stop, + bluetooth_auth_agent_send_response };
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 fa5b8e70066..5810d58e137 100644 --- a/dlls/winebth.sys/unixlib.h +++ b/dlls/winebth.sys/unixlib.h @@ -152,6 +152,12 @@ struct bluetooth_auth_agent_stop_params UINT_PTR auth_agent; };
+struct bluetooth_auth_agent_send_response_params +{ + UINT_PTR agent; + const struct winebluetooth_auth_response *response; +}; + enum bluetoothapis_funcs { unix_bluetooth_init, @@ -177,6 +183,7 @@ enum bluetoothapis_funcs unix_bluetooth_auth_agent_init, unix_bluetooth_auth_agent_start, unix_bluetooth_auth_agent_stop, + unix_bluetooth_auth_agent_send_response,
unix_funcs_count }; diff --git a/dlls/winebth.sys/unixlib_priv.h b/dlls/winebth.sys/unixlib_priv.h index 56042a959a9..bbf23b85d3a 100644 --- a/dlls/winebth.sys/unixlib_priv.h +++ b/dlls/winebth.sys/unixlib_priv.h @@ -60,5 +60,7 @@ extern NTSTATUS bluez_auth_agent_register( void *connection, void *ctx, BLUETOOTH_IO_CAPABILITY capabilities ); extern BOOL bluez_auth_agent_unregister( void *connection, void *agent_ctx ); extern UINT_PTR bluez_auth_agent_ctx_init( void ); +extern NTSTATUS bluez_auth_agent_send_response( void *agent_ctx, + const struct winebluetooth_auth_response *response ); #endif /* SONAME_LIBDBUS_1 */ #endif /* __WINE_BLUETOOTHAPIS_UNIXLIB_PRIV_H */ diff --git a/dlls/winebth.sys/winebluetooth.c b/dlls/winebth.sys/winebluetooth.c index 98f0afb9269..94da3762f3b 100644 --- a/dlls/winebth.sys/winebluetooth.c +++ b/dlls/winebth.sys/winebluetooth.c @@ -217,6 +217,21 @@ NTSTATUS winebluetooth_auth_agent_stop( winebluetooth_auth_agent_t agent ) return status; }
+NTSTATUS winebluetooth_send_auth_response( winebluetooth_auth_agent_t agent, + const struct winebluetooth_auth_response *response ) +{ + NTSTATUS status; + struct bluetooth_auth_agent_send_response_params params = {0}; + + TRACE("(%p, %p)\n", agent.handle, response ); + + params.agent = agent.handle; + params.response = response; + + status = UNIX_BLUETOOTH_CALL( bluetooth_auth_agent_send_response, ¶ms ); + return status; +} + NTSTATUS winebluetooth_init( void ) { NTSTATUS status; diff --git a/dlls/winebth.sys/winebluetooth.h b/dlls/winebth.sys/winebluetooth.h index 5efb3527ad0..f76061e05dc 100644 --- a/dlls/winebth.sys/winebluetooth.h +++ b/dlls/winebth.sys/winebluetooth.h @@ -291,6 +291,16 @@ union winebluetooth_auth_event_data } keypress; };
+struct winebluetooth_auth_response +{ + BLUETOOTH_AUTHENTICATION_METHOD auth_method; + union winebluetooth_auth_event_data auth_data; + BOOL negative_response; +}; + +NTSTATUS winebluetooth_send_auth_response( winebluetooth_auth_agent_t agent, + const struct winebluetooth_auth_response *response ); + enum winebluetooth_auth_event_type { BLUETOOTH_AUTH_EVENT_DEVICE_AUTH_REQUEST, diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index 59256369870..e67238e2208 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -243,6 +243,26 @@ static NTSTATUS WINAPI dispatch_bluetooth(DEVICE_OBJECT *device, IRP *irp ) ext->auth_agent_registered = !status ? FALSE : TRUE; break; } + case IOCTL_WINEBTH_SEND_AUTH_RESPONSE: + { + const struct winebth_authentication_response *response = + (struct winebth_authentication_response *)irp->AssociatedIrp.SystemBuffer; + struct winebluetooth_auth_response resp = {0}; + + resp.auth_method = response->auth_method; + resp.negative_response = response->auth_method; + if (response->auth_method == BLUETOOTH_AUTHENTICATION_METHOD_LEGACY) + { + memcpy( resp.auth_data.pincode, response->auth_data.pin.pin, + sizeof( response->auth_data.pin.pin ) ); + resp.auth_data.pincode[response->auth_data.pin.length] = '\0'; + } + else + resp.auth_data.passkey = response->auth_data.passkey; + + status = winebluetooth_send_auth_response( auth_agent, &resp ); + break; + }; default: break; } diff --git a/include/bthioctl.h b/include/bthioctl.h index f858f30c93e..5c0db3db413 100644 --- a/include/bthioctl.h +++ b/include/bthioctl.h @@ -48,6 +48,7 @@ #define IOCTL_WINEBTH_RADIO_STOP_DISCOVERY CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xa7, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_WINEBTH_START_AUTH_AGENT CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xa8, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_WINEBTH_STOP_AUTH_AGENT CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xa9, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_WINEBTH_SEND_AUTH_RESPONSE CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xaa, METHOD_BUFFERED, FILE_ANY_ACCESS) #endif
#define LOCAL_RADIO_DISCOVERABLE (0x0001) @@ -82,6 +83,23 @@ struct winebth_discovery_filter unsigned int bredr : 1; unsigned int le : 1; }; + +struct winebth_authentication_response +{ + BTH_ADDR address; + BLUETOOTH_AUTHENTICATION_METHOD auth_method; + union + { + ULONG passkey; + struct + { + UCHAR pin[16]; + USHORT length; + } pin; + } auth_data; + BOOL negative_response; +}; + #endif #endif /* __WINE_BTHIOCTL_H_ */
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=148881
Your paranoid android.
=== debian11 (build log) ===
../wine/dlls/winebth.sys/winebth_priv.h:55:5: error: ‘format’ attribute argument 2 value ‘2’ refers to parameter type ‘const WCHAR *’ {aka ‘const short unsigned int *’} ../wine/dlls/winebth.sys/winebth_priv.h:55:5: error: ‘format’ attribute argument 2 value ‘2’ refers to parameter type ‘const WCHAR *’ {aka ‘const short unsigned int *’} ../wine/dlls/winebth.sys/bthenum.c:413:1: error: conflicting types for ‘bthenum_device_props_changed’ ../wine/dlls/winebth.sys/bthenum.c:437:10: error: conflicting types for ‘bthenum_create_fdo_bus’ Task: The win32 Wine build failed
=== debian11b (build log) ===
../wine/dlls/winebth.sys/winebth_priv.h:55:5: error: ‘format’ attribute argument 2 value ‘2’ refers to parameter type ‘const WCHAR *’ {aka ‘const short unsigned int *’} ../wine/dlls/winebth.sys/winebth_priv.h:55:5: error: ‘format’ attribute argument 2 value ‘2’ refers to parameter type ‘const WCHAR *’ {aka ‘const short unsigned int *’} Task: The wow64 Wine build failed