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