-- v8: bluetoothapis/tests: Add tests for BluetoothFindNextDevice. bluetoothapis: Implement BluetoothFindNextDevice. bluetoothapis/tests: Add tests for BluetoothFindFirstDevice, BluetoothFindDeviceClose. bluetoothapis: Implement BluetoothFindFirstDevice and BluetoothFindDeviceClose.
From: Vibhav Pant vibhavp@gmail.com
A Bluetooth address consists of 6 bytes, while a BTH_ADDR is 8 bytes. --- dlls/bluetoothapis/main.c | 2 +- dlls/winebth.sys/winebth.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/dlls/bluetoothapis/main.c b/dlls/bluetoothapis/main.c index 6cdc84f6b6f..87d530c846e 100644 --- a/dlls/bluetoothapis/main.c +++ b/dlls/bluetoothapis/main.c @@ -202,7 +202,7 @@ DWORD WINAPI BluetoothGetRadioInfo( HANDLE radio, PBLUETOOTH_RADIO_INFO info ) return GetLastError();
if (radio_info.localInfo.flags & BDIF_ADDRESS) - info->address.ullLong = RtlUlonglongByteSwap( radio_info.localInfo.address ); + info->address.ullLong = RtlUlonglongByteSwap( radio_info.localInfo.address ) >> 16; if (radio_info.localInfo.flags & BDIF_COD) info->ulClassofDevice = radio_info.localInfo.classOfDevice; if (radio_info.localInfo.flags & BDIF_NAME) diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index b47806de077..c3af6f9b95f 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -122,7 +122,7 @@ static NTSTATUS WINAPI dispatch_bluetooth( DEVICE_OBJECT *device, IRP *irp ) if (ext->props_mask & WINEBLUETOOTH_RADIO_PROPERTY_ADDRESS) { info->localInfo.flags |= BDIF_ADDRESS; - info->localInfo.address = RtlUlonglongByteSwap( ext->props.address.ullLong ); + info->localInfo.address = RtlUlonglongByteSwap( ext->props.address.ullLong ) >> 16; } if (ext->props_mask & WINEBLUETOOTH_RADIO_PROPERTY_NAME) { @@ -190,7 +190,7 @@ static NTSTATUS WINAPI dispatch_bluetooth( DEVICE_OBJECT *device, IRP *irp ) if (device->props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_ADDRESS) { info->flags |= BDIF_ADDRESS; - info->address = RtlUlonglongByteSwap( device->props.address.ullLong ); + info->address = RtlUlonglongByteSwap( device->props.address.ullLong ) >> 16; } if (device->props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_CONNECTED && device->props.connected) @@ -421,7 +421,7 @@ static void update_bluetooth_radio_properties( struct winebluetooth_watcher_even if (event.changed_props_mask & WINEBLUETOOTH_RADIO_PROPERTY_NAME) memcpy( device->props.name, event.props.name, sizeof( event.props.name )); if (event.changed_props_mask & WINEBLUETOOTH_RADIO_PROPERTY_ADDRESS) - device->props.address.ullLong = RtlUlonglongByteSwap( event.props.address.ullLong ); + device->props.address.ullLong = event.props.address.ullLong; if (event.changed_props_mask & WINEBLUETOOTH_RADIO_PROPERTY_DISCOVERABLE) device->props.discoverable = event.props.discoverable; if (event.changed_props_mask & WINEBLUETOOTH_RADIO_PROPERTY_CONNECTABLE) @@ -721,7 +721,7 @@ static void bluetooth_radio_set_properties( DEVICE_OBJECT *obj, { if (mask & WINEBLUETOOTH_RADIO_PROPERTY_ADDRESS) { - BTH_ADDR addr = RtlUlonglongByteSwap( props->address.ullLong ); + BTH_ADDR addr = RtlUlonglongByteSwap( props->address.ullLong ) >> 16; IoSetDevicePropertyData( obj, &DEVPKEY_BluetoothRadio_Address, LOCALE_NEUTRAL, 0, DEVPROP_TYPE_UINT64, sizeof( addr ), &addr ); }
From: Vibhav Pant vibhavp@gmail.com
--- dlls/winebth.sys/dbus.c | 130 +++++++++++++++++++++++++++++++ dlls/winebth.sys/unixlib.c | 9 +++ dlls/winebth.sys/unixlib.h | 6 ++ dlls/winebth.sys/unixlib_priv.h | 1 + dlls/winebth.sys/winebluetooth.c | 10 +++ dlls/winebth.sys/winebth.c | 3 + dlls/winebth.sys/winebth_priv.h | 1 + include/wine/winebth.h | 2 + 8 files changed, 162 insertions(+)
diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index 153c4e60ad1..6bd90008735 100644 --- a/dlls/winebth.sys/dbus.c +++ b/dlls/winebth.sys/dbus.c @@ -183,6 +183,39 @@ static const char *bluez_next_dict_entry( DBusMessageIter *iter, DBusMessageIter return name; }
+/* Adds an entry to a dict iterator of type {sv} */ +static BOOL bluez_variant_dict_add_entry( DBusMessageIter *dict, const char *key, int value_type, + const char *value_type_str, const void *value ) +{ + DBusMessageIter entry, variant; + + if (!p_dbus_message_iter_open_container( dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry )) return FALSE; + if (!p_dbus_message_iter_append_basic( &entry, DBUS_TYPE_STRING, &key )) + { + p_dbus_message_iter_abandon_container( dict, &entry ); + return FALSE; + } + + if (!p_dbus_message_iter_open_container( &entry, DBUS_TYPE_VARIANT, value_type_str, &variant )) + { + p_dbus_message_iter_abandon_container( dict, &entry ); + return FALSE; + } + if (!p_dbus_message_iter_append_basic( &variant, value_type, value )) + { + p_dbus_message_iter_abandon_container( &entry, &variant ); + p_dbus_message_iter_abandon_container( dict, &entry ); + return FALSE; + } + if (!p_dbus_message_iter_close_container( &entry, &variant )) + { + p_dbus_message_iter_abandon_container( dict, &entry ); + return FALSE; + } + + return !!p_dbus_message_iter_close_container( dict, &entry ); +} + static const char *dbgstr_dbus_message( DBusMessage *message ) { const char *interface; @@ -329,6 +362,99 @@ static NTSTATUS bluez_dbus_send_and_wait_for_reply( DBusConnection *connection, return bluez_dbus_pending_call_wait( pending_call, reply, error ); }
+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; + NTSTATUS status; + + 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 ); + if (!p_dbus_message_iter_open_container( &iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter )) + { + p_dbus_message_unref( request ); + return STATUS_NO_MEMORY; + } + if (!bluez_variant_dict_add_entry( &dict_iter, "Transport", DBUS_TYPE_STRING, + DBUS_TYPE_STRING_AS_STRING, &transport_str )) + { + p_dbus_message_iter_abandon_container( &iter, &dict_iter ); + p_dbus_message_unref( request ); + return STATUS_NO_MEMORY; + } + if (!p_dbus_message_iter_close_container( &iter, &dict_iter )) + { + p_dbus_message_unref( request ); + return STATUS_NO_MEMORY; + } + + p_dbus_error_init ( &error ); + + TRACE( "Setting discovery filter on %s to use transport %s\n", debugstr_a( adapter_path ), + debugstr_a( transport_str ) ); + status = bluez_dbus_send_and_wait_for_reply( connection, request, &reply, &error ); + if (status) + { + p_dbus_error_free( &error ); + return status; + } + if (!reply) + { + 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 ) +{ + DBusMessage *request, *reply; + DBusError error; + NTSTATUS status; + + TRACE( "(%p, %s)\n", connection, debugstr_a( adapter_path ) ); + + status = bluez_adapter_set_discovery_filter( connection, adapter_path, "bredr" ); + 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 ); + status = bluez_dbus_send_and_wait_for_reply( connection, request, &reply, &error ); + if (status) + { + p_dbus_error_free( &error ); + return status; + } + if (!reply) + { + ERR( "Failed to 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_set_prop( void *connection, struct bluetooth_adapter_set_prop_params *params ) { DBusMessage *request, *reply; @@ -1560,5 +1686,9 @@ NTSTATUS bluez_adapter_set_prop( void *connection, struct bluetooth_adapter_set_ { return STATUS_NOT_SUPPORTED; } +NTSTATUS bluez_adapter_start_discovery( void *connection, const char *adapter_path ) +{ + return STATUS_NOT_SUPPORTED; +}
#endif /* SONAME_LIBDBUS_1 */ diff --git a/dlls/winebth.sys/unixlib.c b/dlls/winebth.sys/unixlib.c index a80a51d9cf1..9ae1bf4022b 100644 --- a/dlls/winebth.sys/unixlib.c +++ b/dlls/winebth.sys/unixlib.c @@ -171,6 +171,14 @@ static NTSTATUS bluetooth_device_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 ); +} + static NTSTATUS bluetooth_get_event( void *args ) { struct bluetooth_get_event_params *params = args; @@ -186,6 +194,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = {
bluetooth_adapter_set_prop, bluetooth_adapter_get_unique_name, + bluetooth_adapter_start_discovery, bluetooth_adapter_free,
bluetooth_device_free, diff --git a/dlls/winebth.sys/unixlib.h b/dlls/winebth.sys/unixlib.h index 002cc2cc3aa..7de32fd38f0 100644 --- a/dlls/winebth.sys/unixlib.h +++ b/dlls/winebth.sys/unixlib.h @@ -70,6 +70,11 @@ struct bluetooth_adapter_set_prop_params union winebluetooth_property *prop; };
+struct bluetooth_adapter_start_discovery_params +{ + unix_name_t adapter; +}; + struct bluetooth_get_event_params { struct winebluetooth_event result; @@ -82,6 +87,7 @@ enum bluetoothapis_funcs
unix_bluetooth_adapter_set_prop, unix_bluetooth_adapter_get_unique_name, + unix_bluetooth_adapter_start_discovery, unix_bluetooth_adapter_free,
unix_bluetooth_device_free, diff --git a/dlls/winebth.sys/unixlib_priv.h b/dlls/winebth.sys/unixlib_priv.h index 5146799e705..3dc7b956293 100644 --- a/dlls/winebth.sys/unixlib_priv.h +++ b/dlls/winebth.sys/unixlib_priv.h @@ -50,6 +50,7 @@ extern void bluez_dbus_free( void *connection ); extern NTSTATUS bluez_dbus_loop( void *connection, void *watcher_ctx, struct winebluetooth_event *result ); extern NTSTATUS bluez_adapter_set_prop( void *connection, struct bluetooth_adapter_set_prop_params *params ); +extern NTSTATUS bluez_adapter_start_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 /* __WINE_WINEBTH_UNIXLIB_PRIV_H */ diff --git a/dlls/winebth.sys/winebluetooth.c b/dlls/winebth.sys/winebluetooth.c index 6ebfe55ac28..62ae685f55d 100644 --- a/dlls/winebth.sys/winebluetooth.c +++ b/dlls/winebth.sys/winebluetooth.c @@ -68,6 +68,16 @@ NTSTATUS winebluetooth_radio_set_property( winebluetooth_radio_t radio, return UNIX_BLUETOOTH_CALL( bluetooth_adapter_set_prop, ¶ms ); }
+NTSTATUS winebluetooth_radio_start_discovery( winebluetooth_radio_t radio ) +{ + struct bluetooth_adapter_start_discovery_params params = {0}; + + TRACE( "(%p)\n", (void *)radio.handle ); + + params.adapter = radio.handle; + return UNIX_BLUETOOTH_CALL( bluetooth_adapter_start_discovery, ¶ms ); +} + void winebluetooth_radio_free( winebluetooth_radio_t radio ) { struct bluetooth_adapter_free_params args = { 0 }; diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index c3af6f9b95f..2dab1614644 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -251,6 +251,9 @@ static NTSTATUS WINAPI dispatch_bluetooth( DEVICE_OBJECT *device, IRP *irp ) status = winebluetooth_radio_set_property( ext->radio, params->flag, &prop_value ); break; } + case IOCTL_WINEBTH_RADIO_START_DISCOVERY: + status = winebluetooth_radio_start_discovery( ext->radio ); + break; default: FIXME( "Unimplemented IOCTL code: %#lx\n", code ); break; diff --git a/dlls/winebth.sys/winebth_priv.h b/dlls/winebth.sys/winebth_priv.h index b50935c091c..a0d4e689e90 100644 --- a/dlls/winebth.sys/winebth_priv.h +++ b/dlls/winebth.sys/winebth_priv.h @@ -196,6 +196,7 @@ static inline BOOL winebluetooth_radio_equal( winebluetooth_radio_t r1, wineblue NTSTATUS winebluetooth_radio_set_property( winebluetooth_radio_t radio, ULONG prop_flag, union winebluetooth_property *property ); +NTSTATUS winebluetooth_radio_start_discovery( winebluetooth_radio_t radio );
void winebluetooth_device_free( winebluetooth_device_t device ); static inline BOOL winebluetooth_device_equal( winebluetooth_device_t d1, winebluetooth_device_t d2 ) diff --git a/include/wine/winebth.h b/include/wine/winebth.h index c26a2100fb9..222d780e10d 100644 --- a/include/wine/winebth.h +++ b/include/wine/winebth.h @@ -25,6 +25,8 @@ /* Set the discoverability or connectable flag for a local radio. Enabling discoverability will also enable incoming * connections, while disabling incoming connections disables discoverability as well. */ #define IOCTL_WINEBTH_RADIO_SET_FLAG CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xa3, METHOD_BUFFERED, FILE_ANY_ACCESS) +/* Start device inquiry for a local radio. */ +#define IOCTL_WINEBTH_RADIO_START_DISCOVERY CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xa6, METHOD_BUFFERED, FILE_ANY_ACCESS)
#include <pshpack1.h>
From: Vibhav Pant vibhavp@gmail.com
--- dlls/winebth.sys/dbus.c | 37 ++++++++++++++++++++++++++++++++ dlls/winebth.sys/unixlib.c | 9 ++++++++ dlls/winebth.sys/unixlib.h | 6 ++++++ dlls/winebth.sys/unixlib_priv.h | 1 + dlls/winebth.sys/winebluetooth.c | 9 ++++++++ dlls/winebth.sys/winebth.c | 3 +++ dlls/winebth.sys/winebth_priv.h | 1 + include/wine/winebth.h | 2 ++ 8 files changed, 68 insertions(+)
diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index 6bd90008735..383fd74187d 100644 --- a/dlls/winebth.sys/dbus.c +++ b/dlls/winebth.sys/dbus.c @@ -455,6 +455,39 @@ NTSTATUS bluez_adapter_start_discovery( void *connection, const char *adapter_pa return STATUS_SUCCESS; }
+NTSTATUS bluez_adapter_stop_discovery( void *connection, const char *adapter_path ) +{ + DBusMessage *request, *reply; + DBusError error; + NTSTATUS status; + + 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; + + TRACE( "Stopping discovery on %s\n", debugstr_a( adapter_path ) ); + p_dbus_error_init( &error ); + status = bluez_dbus_send_and_wait_for_reply( connection, request, &reply, &error ); + if (status) + { + p_dbus_error_free( &error ); + return status; + } + if (!reply) + { + ERR( "Failed to 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; +} + NTSTATUS bluez_adapter_set_prop( void *connection, struct bluetooth_adapter_set_prop_params *params ) { DBusMessage *request, *reply; @@ -1690,5 +1723,9 @@ NTSTATUS bluez_adapter_start_discovery( void *connection, const char *adapter_pa { return STATUS_NOT_SUPPORTED; } +NTSTATUS bluez_adapter_stop_discovery( void *connection, const char *adapter_path ) +{ + return STATUS_NOT_SUPPORTED; +}
#endif /* SONAME_LIBDBUS_1 */ diff --git a/dlls/winebth.sys/unixlib.c b/dlls/winebth.sys/unixlib.c index 9ae1bf4022b..88f6c6164d3 100644 --- a/dlls/winebth.sys/unixlib.c +++ b/dlls/winebth.sys/unixlib.c @@ -179,6 +179,14 @@ static NTSTATUS bluetooth_adapter_start_discovery( void *args ) return bluez_adapter_start_discovery( dbus_connection, params->adapter->str ); }
+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_get_event( void *args ) { struct bluetooth_get_event_params *params = args; @@ -195,6 +203,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = { bluetooth_adapter_set_prop, bluetooth_adapter_get_unique_name, bluetooth_adapter_start_discovery, + bluetooth_adapter_stop_discovery, bluetooth_adapter_free,
bluetooth_device_free, diff --git a/dlls/winebth.sys/unixlib.h b/dlls/winebth.sys/unixlib.h index 7de32fd38f0..732c76f7d64 100644 --- a/dlls/winebth.sys/unixlib.h +++ b/dlls/winebth.sys/unixlib.h @@ -75,6 +75,11 @@ struct bluetooth_adapter_start_discovery_params unix_name_t adapter; };
+struct bluetooth_adapter_stop_discovery_params +{ + unix_name_t adapter; +}; + struct bluetooth_get_event_params { struct winebluetooth_event result; @@ -88,6 +93,7 @@ enum bluetoothapis_funcs unix_bluetooth_adapter_set_prop, unix_bluetooth_adapter_get_unique_name, unix_bluetooth_adapter_start_discovery, + unix_bluetooth_adapter_stop_discovery, unix_bluetooth_adapter_free,
unix_bluetooth_device_free, diff --git a/dlls/winebth.sys/unixlib_priv.h b/dlls/winebth.sys/unixlib_priv.h index 3dc7b956293..60570a2413a 100644 --- a/dlls/winebth.sys/unixlib_priv.h +++ b/dlls/winebth.sys/unixlib_priv.h @@ -51,6 +51,7 @@ extern NTSTATUS bluez_dbus_loop( void *connection, void *watcher_ctx, struct win 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 ); +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 /* __WINE_WINEBTH_UNIXLIB_PRIV_H */ diff --git a/dlls/winebth.sys/winebluetooth.c b/dlls/winebth.sys/winebluetooth.c index 62ae685f55d..370f102d6f3 100644 --- a/dlls/winebth.sys/winebluetooth.c +++ b/dlls/winebth.sys/winebluetooth.c @@ -78,6 +78,15 @@ NTSTATUS winebluetooth_radio_start_discovery( winebluetooth_radio_t radio ) 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", (void *)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/winebth.c b/dlls/winebth.sys/winebth.c index 2dab1614644..94aa2869b72 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -254,6 +254,9 @@ static NTSTATUS WINAPI dispatch_bluetooth( DEVICE_OBJECT *device, IRP *irp ) case IOCTL_WINEBTH_RADIO_START_DISCOVERY: status = winebluetooth_radio_start_discovery( ext->radio ); break; + case IOCTL_WINEBTH_RADIO_STOP_DISCOVERY: + status = winebluetooth_radio_stop_discovery( ext->radio ); + break; default: FIXME( "Unimplemented IOCTL code: %#lx\n", code ); break; diff --git a/dlls/winebth.sys/winebth_priv.h b/dlls/winebth.sys/winebth_priv.h index a0d4e689e90..e664043eb63 100644 --- a/dlls/winebth.sys/winebth_priv.h +++ b/dlls/winebth.sys/winebth_priv.h @@ -197,6 +197,7 @@ NTSTATUS winebluetooth_radio_set_property( winebluetooth_radio_t radio, ULONG prop_flag, union winebluetooth_property *property ); NTSTATUS winebluetooth_radio_start_discovery( winebluetooth_radio_t radio ); +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, winebluetooth_device_t d2 ) diff --git a/include/wine/winebth.h b/include/wine/winebth.h index 222d780e10d..d05e4cad529 100644 --- a/include/wine/winebth.h +++ b/include/wine/winebth.h @@ -27,6 +27,8 @@ #define IOCTL_WINEBTH_RADIO_SET_FLAG CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xa3, METHOD_BUFFERED, FILE_ANY_ACCESS) /* Start device inquiry for a local radio. */ #define IOCTL_WINEBTH_RADIO_START_DISCOVERY CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xa6, METHOD_BUFFERED, FILE_ANY_ACCESS) +/* Stop device inquiry for a local radio. */ +#define IOCTL_WINEBTH_RADIO_STOP_DISCOVERY CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xa7, METHOD_BUFFERED, FILE_ANY_ACCESS)
#include <pshpack1.h>
From: Vibhav Pant vibhavp@gmail.com
--- dlls/bluetoothapis/main.c | 195 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 186 insertions(+), 9 deletions(-)
diff --git a/dlls/bluetoothapis/main.c b/dlls/bluetoothapis/main.c index 87d530c846e..a3741f87afa 100644 --- a/dlls/bluetoothapis/main.c +++ b/dlls/bluetoothapis/main.c @@ -48,15 +48,181 @@ struct bluetooth_find_radio_handle DWORD idx; };
+struct bluetooth_find_device_handle +{ + BLUETOOTH_DEVICE_SEARCH_PARAMS params; + BTH_DEVICE_INFO_LIST *device_list; + SIZE_T idx; +}; + +static const char *debugstr_BLUETOOTH_DEVICE_SEARCH_PARAMS( const BLUETOOTH_DEVICE_SEARCH_PARAMS *params ) +{ + if (!params || params->dwSize != sizeof( *params )) + return wine_dbg_sprintf("%p", params ); + + return wine_dbg_sprintf( "{ %ld %d %d %d %d %d %u %p }", params->dwSize, params->fReturnAuthenticated, + params->fReturnRemembered, params->fReturnUnknown, params->fReturnConnected, + params->fIssueInquiry, params->cTimeoutMultiplier, params->hRadio ); +} + +static BOOL radio_set_inquiry( HANDLE radio, BOOL enable ) +{ + DWORD bytes; + + return DeviceIoControl( radio, enable ? IOCTL_WINEBTH_RADIO_START_DISCOVERY : IOCTL_WINEBTH_RADIO_STOP_DISCOVERY, + NULL, 0, NULL, 0, &bytes, NULL ); +} + +static BTH_DEVICE_INFO_LIST *radio_get_devices( HANDLE radio ) +{ + DWORD num_devices = 1; + + for (;;) + { + BTH_DEVICE_INFO_LIST *list; + DWORD size, bytes; + + size = sizeof( *list ) + (num_devices - 1) * sizeof( list->deviceList[0] ); + list = calloc( 1, size ); + if (!list) + { + SetLastError( ERROR_OUTOFMEMORY ); + return NULL; + } + if (!DeviceIoControl( radio, IOCTL_BTH_GET_DEVICE_INFO, NULL, 0, list, size, &bytes, NULL ) + && GetLastError() != ERROR_INVALID_USER_BUFFER) + { + free( list ); + return NULL; + } + if (!list->numOfDevices) + { + free( list ); + SetLastError( ERROR_NO_MORE_ITEMS ); + return NULL; + } + if (num_devices != list->numOfDevices) + { + /* The buffer is incorrectly sized, try again. */ + num_devices = list->numOfDevices; + free( list ); + continue; + } + return list; + } +} + +static void device_info_from_bth_info( BLUETOOTH_DEVICE_INFO *info, const BTH_DEVICE_INFO *bth_info ) +{ + memset( info, 0, sizeof( *info )); + info->dwSize = sizeof( *info ); + + if (bth_info->flags & BDIF_ADDRESS) + info->Address.ullLong = bth_info->address; + info->ulClassofDevice = bth_info->classOfDevice; + info->fConnected = !!(bth_info->flags & BDIF_CONNECTED); + info->fRemembered = !!(bth_info->flags & BDIF_PERSONAL); + info->fAuthenticated = !!(bth_info->flags & BDIF_PAIRED); + + if (bth_info->flags & BDIF_NAME) + MultiByteToWideChar( CP_ACP, 0, bth_info->name, -1, info->szName, ARRAY_SIZE( info->szName ) ); +} + +static BOOL device_find_next( struct bluetooth_find_device_handle *find, BLUETOOTH_DEVICE_INFO *info ) +{ + while (find->idx < find->device_list->numOfDevices) + { + const BTH_DEVICE_INFO *bth_info; + BOOL matches; + + bth_info = &find->device_list->deviceList[find->idx++]; + matches = (find->params.fReturnAuthenticated && bth_info->flags & BDIF_PAIRED) || + (find->params.fReturnRemembered && bth_info->flags & BDIF_PERSONAL) || + (find->params.fReturnUnknown && !(bth_info->flags & BDIF_PERSONAL)) || + (find->params.fReturnConnected && bth_info->flags & BDIF_CONNECTED); + + if (!matches) + continue; + + device_info_from_bth_info( info, bth_info ); + /* If the user is looking for unknown devices as part of device inquiry, or wants connected devices, + * we can set stLastSeen to the current UTC time. */ + if ((find->params.fIssueInquiry && find->params.fReturnUnknown && !info->fRemembered) + || (find->params.fReturnConnected && info->fConnected)) + GetSystemTime( &info->stLastSeen ); + else + FIXME( "semi-stub!\n" ); + return TRUE; + } + + return FALSE; +} + /********************************************************************* * BluetoothFindFirstDevice */ -HBLUETOOTH_DEVICE_FIND WINAPI BluetoothFindFirstDevice(BLUETOOTH_DEVICE_SEARCH_PARAMS *params, - BLUETOOTH_DEVICE_INFO *info) +HBLUETOOTH_DEVICE_FIND WINAPI BluetoothFindFirstDevice( BLUETOOTH_DEVICE_SEARCH_PARAMS *params, + BLUETOOTH_DEVICE_INFO *info ) { - FIXME("(%p %p): stub!\n", params, info); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return NULL; + struct bluetooth_find_device_handle *hfind; + BTH_DEVICE_INFO_LIST *device_list; + + TRACE( "(%s %p)\n", debugstr_BLUETOOTH_DEVICE_SEARCH_PARAMS( params ), info ); + + if (!params || !info) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return NULL; + } + if (params->dwSize != sizeof( *params ) || info->dwSize != sizeof( *info )) + { + SetLastError( ERROR_REVISION_MISMATCH ); + return NULL; + } + if (params->fIssueInquiry && params->cTimeoutMultiplier > 48) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return NULL; + } + if (!params->hRadio) + { + /* TODO: Perform device inquiry on all local radios */ + FIXME( "semi-stub!\n" ); + SetLastError( ERROR_INVALID_PARAMETER ); + return NULL; + } + + if (params->fIssueInquiry) + { + if (!radio_set_inquiry( params->hRadio, TRUE )) + return NULL; + Sleep( params->cTimeoutMultiplier * 1280 ); + radio_set_inquiry( params->hRadio, FALSE ); + } + + if (!(device_list = radio_get_devices( params->hRadio ))) + return NULL; + + hfind = malloc( sizeof( *hfind ) ); + if (!hfind) + { + free( device_list ); + SetLastError( ERROR_OUTOFMEMORY ); + return NULL; + } + + hfind->params = *params; + hfind->device_list = device_list; + hfind->idx = 0; + if (!device_find_next( hfind, info )) + { + free( hfind->device_list ); + free( hfind ); + SetLastError( ERROR_NO_MORE_ITEMS ); + return NULL; + } + SetLastError( ERROR_SUCCESS ); + return hfind; }
/********************************************************************* @@ -130,11 +296,22 @@ BOOL WINAPI BluetoothFindRadioClose( HBLUETOOTH_RADIO_FIND find_handle ) /********************************************************************* * BluetoothFindDeviceClose */ -BOOL WINAPI BluetoothFindDeviceClose(HBLUETOOTH_DEVICE_FIND find) +BOOL WINAPI BluetoothFindDeviceClose( HBLUETOOTH_DEVICE_FIND find_handle ) { - FIXME("(%p): stub!\n", find); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; + struct bluetooth_find_device_handle *find = find_handle; + + TRACE( "(%p)\n", find_handle ); + + if (!find_handle) + { + SetLastError( ERROR_INVALID_HANDLE ); + return FALSE; + } + + free( find->device_list ); + free( find ); + SetLastError( ERROR_SUCCESS ); + return TRUE; }
/*********************************************************************
From: Vibhav Pant vibhavp@gmail.com
--- dlls/bluetoothapis/tests/Makefile.in | 1 + dlls/bluetoothapis/tests/device.c | 110 +++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 dlls/bluetoothapis/tests/device.c
diff --git a/dlls/bluetoothapis/tests/Makefile.in b/dlls/bluetoothapis/tests/Makefile.in index c7d6567f578..5d01bf3a809 100644 --- a/dlls/bluetoothapis/tests/Makefile.in +++ b/dlls/bluetoothapis/tests/Makefile.in @@ -2,5 +2,6 @@ TESTDLL = bluetoothapis.dll IMPORTS = bluetoothapis
SOURCES = \ + device.c \ radio.c \ sdp.c diff --git a/dlls/bluetoothapis/tests/device.c b/dlls/bluetoothapis/tests/device.c new file mode 100644 index 00000000000..716b2d3f31a --- /dev/null +++ b/dlls/bluetoothapis/tests/device.c @@ -0,0 +1,110 @@ +/* + * Tests for Bluetooth device methods + * + * Copyright 2025 Vibhav Pant + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <stdarg.h> + +#include <windef.h> +#include <winbase.h> + +#include <bthsdpdef.h> +#include <bluetoothapis.h> + +#include <wine/test.h> + +extern void test_for_all_radios( void (*test)( HANDLE radio, void *data ), void *data ); + +void test_radio_BluetoothFindFirstDevice( HANDLE radio, void *data ) +{ + BLUETOOTH_DEVICE_SEARCH_PARAMS search_params; + BLUETOOTH_DEVICE_INFO device_info; + HBLUETOOTH_DEVICE_FIND hfind; + DWORD err, exp; + BOOL success; + + search_params.dwSize = sizeof( search_params ); + search_params.cTimeoutMultiplier = 200; + search_params.fIssueInquiry = TRUE; + search_params.fReturnUnknown = TRUE; + search_params.hRadio = radio; + device_info.dwSize = sizeof( device_info ); + SetLastError( 0xdeadbeef ); + hfind = BluetoothFindFirstDevice( &search_params, &device_info ); + ok( !hfind, "Expected %p to be NULL\n", hfind ); + err = GetLastError(); + ok( err == ERROR_INVALID_PARAMETER, "%lu != %d\n", err, ERROR_INVALID_PARAMETER ); + + search_params.cTimeoutMultiplier = 5; + search_params.fIssueInquiry = winetest_interactive; + SetLastError( 0xdeadbeef ); + hfind = BluetoothFindFirstDevice( &search_params, &device_info ); + err = GetLastError(); + exp = hfind ? ERROR_SUCCESS : ERROR_NO_MORE_ITEMS; + todo_wine_if( !radio ) ok( err == exp, "%lu != %lu\n", err, exp ); + + if (hfind) + { + success = BluetoothFindDeviceClose( hfind ); + ok( success, "BluetoothFindDeviceClose failed: %lu\n", GetLastError() ); + } +} + +void test_BluetoothFindFirstDevice( void ) +{ + BLUETOOTH_DEVICE_SEARCH_PARAMS search_params = {0}; + BLUETOOTH_DEVICE_INFO device_info = {0}; + HBLUETOOTH_DEVICE_FIND hfind; + DWORD err; + + SetLastError( 0xdeadbeef ); + hfind = BluetoothFindFirstDevice( NULL, NULL ); + ok( !hfind, "Expected %p to be NULL\n", hfind ); + err = GetLastError(); + ok( err == ERROR_INVALID_PARAMETER, "%lu != %d\n", err, ERROR_INVALID_PARAMETER ); + + SetLastError( 0xdeadbeef ); + hfind = BluetoothFindFirstDevice( &search_params, NULL ); + ok( !hfind, "Expected %p to be NULL\n", hfind ); + err = GetLastError(); + ok( err == ERROR_INVALID_PARAMETER, "%lu != %d\n", err, ERROR_INVALID_PARAMETER ); + + SetLastError( 0xdeadbeef ); + hfind = BluetoothFindFirstDevice( &search_params, &device_info ); + ok( !hfind, "Expected %p to be NULL\n", hfind ); + err = GetLastError(); + ok( err == ERROR_REVISION_MISMATCH, "%lu != %d\n", err, ERROR_REVISION_MISMATCH ); + + test_for_all_radios( test_radio_BluetoothFindFirstDevice, NULL ); +} + +void test_BluetoothFindDeviceClose( void ) +{ + DWORD err; + + SetLastError( 0xdeadbeef ); + ok( !BluetoothFindDeviceClose( NULL ), "Expected BluetoothFindDeviceClose to return FALSE\n" ); + err = GetLastError(); + ok( err == ERROR_INVALID_HANDLE, "%lu != %d\n", err, ERROR_INVALID_HANDLE ); +} + +START_TEST( device ) +{ + test_BluetoothFindFirstDevice(); + test_BluetoothFindDeviceClose(); +}
From: Vibhav Pant vibhavp@gmail.com
--- dlls/bluetoothapis/main.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-)
diff --git a/dlls/bluetoothapis/main.c b/dlls/bluetoothapis/main.c index a3741f87afa..9e361778169 100644 --- a/dlls/bluetoothapis/main.c +++ b/dlls/bluetoothapis/main.c @@ -214,7 +214,7 @@ HBLUETOOTH_DEVICE_FIND WINAPI BluetoothFindFirstDevice( BLUETOOTH_DEVICE_SEARCH_ hfind->params = *params; hfind->device_list = device_list; hfind->idx = 0; - if (!device_find_next( hfind, info )) + if (!BluetoothFindNextDevice( hfind, info )) { free( hfind->device_list ); free( hfind ); @@ -586,11 +586,25 @@ BOOL WINAPI BluetoothEnableDiscovery( HANDLE radio, BOOL enabled ) /********************************************************************* * BluetoothFindNextDevice */ -BOOL WINAPI BluetoothFindNextDevice(HBLUETOOTH_DEVICE_FIND find, BLUETOOTH_DEVICE_INFO *info) +BOOL WINAPI BluetoothFindNextDevice( HBLUETOOTH_DEVICE_FIND find, BLUETOOTH_DEVICE_INFO *info ) { - FIXME("(%p, %p): stub!\n", find, info); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; + BOOL success; + + TRACE( "(%p, %p)\n", find, info ); + + /* This method doesn't perform any validation for info, for some reason. */ + if (!find) + { + SetLastError( ERROR_INVALID_HANDLE ); + return FALSE; + } + + success = device_find_next( find, info ); + if (!success) + SetLastError( ERROR_NO_MORE_ITEMS ); + else + SetLastError( ERROR_SUCCESS ); + return success; }
/*********************************************************************
From: Vibhav Pant vibhavp@gmail.com
--- dlls/bluetoothapis/tests/device.c | 82 +++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+)
diff --git a/dlls/bluetoothapis/tests/device.c b/dlls/bluetoothapis/tests/device.c index 716b2d3f31a..b9331b7488d 100644 --- a/dlls/bluetoothapis/tests/device.c +++ b/dlls/bluetoothapis/tests/device.c @@ -22,6 +22,7 @@
#include <windef.h> #include <winbase.h> +#include <winnls.h>
#include <bthsdpdef.h> #include <bluetoothapis.h> @@ -93,6 +94,86 @@ void test_BluetoothFindFirstDevice( void ) test_for_all_radios( test_radio_BluetoothFindFirstDevice, NULL ); }
+void test_radio_BluetoothFindNextDevice( HANDLE radio, void *data ) +{ + BLUETOOTH_DEVICE_SEARCH_PARAMS search_params = *(BLUETOOTH_DEVICE_SEARCH_PARAMS *)data; + BLUETOOTH_DEVICE_INFO info = {0}; + HBLUETOOTH_DEVICE_FIND hfind; + BOOL success; + DWORD err, i = 0; + + search_params.hRadio = radio; + info.dwSize = sizeof( info ); + + SetLastError( 0xdeadbeef ); + hfind = BluetoothFindFirstDevice( &search_params, &info ); + err = GetLastError(); + ok( !!hfind || err == ERROR_NO_MORE_ITEMS, "BluetoothFindFirstDevice failed: %lu\n", GetLastError() ); + if (!hfind) + { + skip( "No devices found.\n" ); + return; + } + + for (;;) + { + const BYTE *addr = info.Address.rgBytes; + WCHAR buf[256]; + BOOL matches; + + matches = (info.fConnected && search_params.fReturnConnected) + || (info.fAuthenticated && search_params.fReturnAuthenticated) + || (info.fRemembered && search_params.fReturnRemembered) + || (!info.fRemembered && search_params.fReturnUnknown); + ok( matches, "Device does not match filter constraints\n" ); + trace( "device %lu: %02x:%02x:%02x:%02x:%02x:%02x\n", i, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5] ); + trace( " name: %s\n", debugstr_w( info.szName ) ); + trace( " class: %#lx\n", info.ulClassofDevice ); + trace( " connected: %d, authenticated: %d, remembered: %d\n", info.fConnected, info.fAuthenticated, + info.fRemembered ); + if (GetTimeFormatEx( NULL, TIME_FORCE24HOURFORMAT, &info.stLastSeen, NULL, buf, ARRAY_SIZE( buf ) )) + trace( " last seen: %s UTC\n", debugstr_w( buf ) ); + + memset( &info, 0, sizeof( info )); + info.dwSize = sizeof( info ); + SetLastError( 0xdeadbeef ); + success = BluetoothFindNextDevice( hfind, &info ); + err = GetLastError(); + ok( success || err == ERROR_NO_MORE_ITEMS, "BluetoothFindNextDevice failed: %lu\n", err ); + if (!success) + break; + } + + ok( BluetoothFindDeviceClose( hfind ), "BluetoothFindDeviceClose failed: %lu\n", GetLastError() ); +} + +void test_BluetoothFindNextDevice( void ) +{ + BLUETOOTH_DEVICE_SEARCH_PARAMS params = {0}; + DWORD err; + BOOL ret; + + SetLastError( 0xdeadbeef ); + ret = BluetoothFindNextDevice( NULL, NULL ); + ok( !ret, "Expected BluetoothFindNextDevice to return FALSE\n" ); + err = GetLastError(); + ok( err == ERROR_INVALID_HANDLE, "%lu != %d\n", err, ERROR_INVALID_HANDLE ); + + params.dwSize = sizeof( params ); + params.fReturnUnknown = TRUE; + params.fReturnRemembered = TRUE; + params.fReturnConnected = TRUE; + params.fReturnAuthenticated = TRUE; + + if (winetest_interactive) + { + params.fIssueInquiry = TRUE; + params.cTimeoutMultiplier = 5; + } + + test_for_all_radios( test_radio_BluetoothFindNextDevice, ¶ms ); +} + void test_BluetoothFindDeviceClose( void ) { DWORD err; @@ -107,4 +188,5 @@ START_TEST( device ) { test_BluetoothFindFirstDevice(); test_BluetoothFindDeviceClose(); + test_BluetoothFindNextDevice(); }
On Fri Mar 7 14:32:20 2025 +0000, Vibhav Pant wrote:
changed this line in [version 8 of the diff](/wine/wine/-/merge_requests/7472/diffs?diff_id=162476&start_sha=02fe1bdb76d3f1b03d30c286561af36cd98f6031#98ba129d60a0731392e00055632a28e4be4414aa_144_131)
Makes sense, done.
On Fri Mar 7 14:32:21 2025 +0000, Vibhav Pant wrote:
changed this line in [version 8 of the diff](/wine/wine/-/merge_requests/7472/diffs?diff_id=162476&start_sha=02fe1bdb76d3f1b03d30c286561af36cd98f6031#98ba129d60a0731392e00055632a28e4be4414aa_118_85)
Yes, this was a little error prone. I have rewritten this function.
On Fri Mar 7 14:32:21 2025 +0000, Vibhav Pant wrote:
changed this line in [version 8 of the diff](/wine/wine/-/merge_requests/7472/diffs?diff_id=162476&start_sha=02fe1bdb76d3f1b03d30c286561af36cd98f6031#98ba129d60a0731392e00055632a28e4be4414aa_92_85)
The MSDN documentation suggests an "input buffer length", which is why I passed them. But yes, it's unnecessary, even the winebth.sys does not use them. Fixed
On Fri Mar 7 14:32:21 2025 +0000, Vibhav Pant wrote:
changed this line in [version 8 of the diff](/wine/wine/-/merge_requests/7472/diffs?diff_id=162476&start_sha=02fe1bdb76d3f1b03d30c286561af36cd98f6031#98ba129d60a0731392e00055632a28e4be4414aa_102_85)
Should be simplified in the rewritten function.
On Fri Mar 7 14:32:22 2025 +0000, Vibhav Pant wrote:
changed this line in [version 8 of the diff](/wine/wine/-/merge_requests/7472/diffs?diff_id=162476&start_sha=02fe1bdb76d3f1b03d30c286561af36cd98f6031#98ba129d60a0731392e00055632a28e4be4414aa_212_195)
Yup, fixed. Thanks
On Thu Mar 6 22:58:48 2025 +0000, Elizabeth Figura wrote:
Why do this with a separate function instead of inside test_radio_BluetoothFindFirstDevice()?
Mostly for consistency's sake. This is the same pattern we use for testing `BluetoothFindFirstRadio`,.`BluetoothFindNextRadio` and `BluetoothFindRadioClose` as well.
On Thu Mar 6 22:58:46 2025 +0000, Elizabeth Figura wrote:
Isn't this going to always wait for 6 seconds? Maybe we should guard this behind winetest_interactive or something.
Ah, I wasn't aware of this flag. Fixed, thanks.
On Thu Mar 6 22:58:46 2025 +0000, Elizabeth Figura wrote:
Calling this a "handle" seems odd when it *is* the thing the handle points to.
Yeah, the naming for the radio search handle is odd too. Would context be a better suffix, at the cost of perhaps being a little too generic?
On Fri Mar 7 19:49:48 2025 +0000, Vibhav Pant wrote:
Yeah, the naming for the radio search handle is odd too. Would context be a better suffix, at the cost of perhaps being a little too generic?
Yes, that seems better, or even just "bluetooth_device_find" to match the handle name.