From: Vibhav Pant <vibhavp@gmail.com> --- dlls/winebth.sys/dbus.c | 86 ++++++++++++++++++++++++++++++-- dlls/winebth.sys/unixlib.c | 17 +++++++ dlls/winebth.sys/unixlib.h | 13 +++++ dlls/winebth.sys/unixlib_priv.h | 2 + dlls/winebth.sys/winebluetooth.c | 38 +++++++++++++- dlls/winebth.sys/winebth.c | 16 ++++++ dlls/winebth.sys/winebth_priv.h | 21 ++++++++ include/bthledef.h | 6 +++ 8 files changed, 193 insertions(+), 6 deletions(-) diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index 3dcd108c393..881621e8ca9 100644 --- a/dlls/winebth.sys/dbus.c +++ b/dlls/winebth.sys/dbus.c @@ -1,8 +1,7 @@ /* * Support for communicating with BlueZ over DBus. * - * Copyright 2024 Vibhav Pant - * Copyright 2025 Vibhav Pant + * Copyright 2024-2026 Vibhav Pant * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -984,6 +983,56 @@ static NTSTATUS bluez_device_get_props_by_path_async( DBusConnection *connection return STATUS_SUCCESS; } +struct bluez_gatt_characteristic_value +{ + DBusMessage *message; + const BYTE *buf; /* Points into message */ +}; + +/* bytes_iter must be _inside_ the byte array (see the documentation for dbus_message_iter_get_fixed_array). + * message should point to the DBus message containing the byte array value. */ +static BOOL bluez_gatt_characteristic_value_new_from_iter( DBusMessage *message, DBusMessageIter *bytes_iter, + struct winebluetooth_gatt_characteristic_value *value ) +{ + const BYTE *buf; + int size; + + TRACE_( dbus )( "(%s, %s, %p)\n", dbgstr_dbus_message( message ), dbgstr_dbus_iter( bytes_iter ), value ); + + p_dbus_message_iter_get_fixed_array( bytes_iter, &buf, &size ); + value->size = size; + if (size > sizeof( value->buf )) + { + struct bluez_gatt_characteristic_value *dbus_val; + + if (!(dbus_val = calloc( 1, sizeof( *dbus_val ) ))) + return FALSE; + dbus_val->buf = buf; + dbus_val->message = p_dbus_message_ref( message ); + value->handle = (UINT_PTR)dbus_val; + } + else if (size) + memcpy( value->buf, buf, size ); + return TRUE; +} + +void bluez_gatt_characteristic_value_free( void *val ) +{ + struct bluez_gatt_characteristic_value *value = val; + + p_dbus_message_unref( value->message ); + free( value ); +} + +void bluez_gatt_characteristic_value_move( struct winebluetooth_gatt_characteristic_value *value, BYTE *dest ) +{ + struct bluez_gatt_characteristic_value *dbus_val = (struct bluez_gatt_characteristic_value *)value->handle; + + assert( !winebluetooth_gatt_characteristic_value_is_inline( value ) ); + memcpy( dest, value->buf, value->size ); + bluez_gatt_characteristic_value_free( dbus_val ); +} + struct bluez_watcher_ctx { void *init_device_list_call; @@ -2185,6 +2234,9 @@ static void bluez_watcher_free( struct bluez_watcher_ctx *watcher ) case BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_ADDED: unix_name_free( (struct unix_name *)event1->event.gatt_characteristic_added.characteristic.handle ); unix_name_free( (struct unix_name *)event1->event.gatt_characteristic_added.service.handle ); + if (!winebluetooth_gatt_characteristic_value_is_inline( &event1->event.gatt_characteristic_added.value )) + bluez_gatt_characteristic_value_free( + (struct bluez_gatt_characteristic_value *)event1->event.gatt_characteristic_added.value.handle ); break; case BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_REMOVED: unix_name_free( (struct unix_name *)event1->event.gatt_characterisic_removed.handle ); @@ -2439,10 +2491,31 @@ static NTSTATUS bluez_build_initial_device_lists( DBusMessage *reply, struct lis init_entry->object.characteristic.characteristic.handle = (UINT_PTR)char_name; while ((prop_name = bluez_next_dict_entry( &prop_iter, &variant ))) - bluez_gatt_characteristic_props_from_dict_entry( prop_name, &variant, - &init_entry->object.characteristic ); + { + if (!strcmp( prop_name, "Value" ) + && p_dbus_message_iter_get_arg_type( &variant ) == DBUS_TYPE_ARRAY + && p_dbus_message_iter_get_element_type( &variant ) == DBUS_TYPE_BYTE) + { + DBusMessageIter bytes_iter; + p_dbus_message_iter_recurse( &variant, &bytes_iter ); + if (!bluez_gatt_characteristic_value_new_from_iter( reply, &bytes_iter, + &init_entry->object.characteristic.value )) + { + unix_name_free( char_name ); + free( init_entry ); + status = STATUS_NO_MEMORY; + goto done; + } + } + else + bluez_gatt_characteristic_props_from_dict_entry( prop_name, &variant, + &init_entry->object.characteristic ); + } if (!init_entry->object.characteristic.service.handle) { + if (!winebluetooth_gatt_characteristic_value_is_inline( &init_entry->object.characteristic.value )) + bluez_gatt_characteristic_value_free( + (struct bluez_gatt_characteristic_value *)init_entry->object.characteristic.value.handle ); unix_name_free( char_name ); free( init_entry ); ERR( "Could not find the associated service for the GATT charcteristic %s\n", debugstr_a( path ) ); @@ -2661,5 +2734,10 @@ NTSTATUS bluez_device_start_pairing( void *connection, void *watcher_ctx, struct { return STATUS_NOT_SUPPORTED; } +void bluez_gatt_characteristic_value_move( struct winebluetooth_gatt_characteristic_value *value, BYTE *buf ) +{ + return STATUS_NOT_SUPPORTED; +} +void bluez_gatt_characteristic_value_free( void *val ) { return STATUS_NOT_SUPPORTED; } #endif /* SONAME_LIBDBUS_1 */ diff --git a/dlls/winebth.sys/unixlib.c b/dlls/winebth.sys/unixlib.c index 0045db54bcf..c11000859bd 100644 --- a/dlls/winebth.sys/unixlib.c +++ b/dlls/winebth.sys/unixlib.c @@ -276,6 +276,21 @@ static NTSTATUS bluetooth_gatt_characteristic_free( void *args ) return STATUS_SUCCESS; } +static NTSTATUS bluetooth_gatt_characteristic_value_move( void *args ) +{ + struct bluetooth_gatt_characteristic_value_move_params *params = args; + if (!dbus_connection) return STATUS_NOT_SUPPORTED; + bluez_gatt_characteristic_value_move( params->val, params->buf ); + return STATUS_SUCCESS; +} + +static NTSTATUS bluetooth_gatt_characteristic_value_free( void *args ) +{ + struct bluetooth_gatt_characteristic_value_free_params *params = args; + if (!dbus_connection) return STATUS_NOT_SUPPORTED; + bluez_gatt_characteristic_value_free( params->val ); + return STATUS_SUCCESS; +} static NTSTATUS bluetooth_get_event( void *args ) { @@ -309,6 +324,8 @@ const unixlib_entry_t __wine_unix_call_funcs[] = { bluetooth_gatt_service_free, bluetooth_gatt_characteristic_free, + bluetooth_gatt_characteristic_value_move, + bluetooth_gatt_characteristic_value_free, bluetooth_get_event, }; diff --git a/dlls/winebth.sys/unixlib.h b/dlls/winebth.sys/unixlib.h index 765003a5fc3..2b2ecd919de 100644 --- a/dlls/winebth.sys/unixlib.h +++ b/dlls/winebth.sys/unixlib.h @@ -74,6 +74,17 @@ struct bluetooth_gatt_characteristic_free_params unix_name_t characteristic; }; +struct bluetooth_gatt_characteristic_value_move_params +{ + struct winebluetooth_gatt_characteristic_value *val; + BYTE *buf; +}; + +struct bluetooth_gatt_characteristic_value_free_params +{ + struct winebluetooth_gatt_characteristic_value *val; +}; + struct bluetooth_device_disconnect_params { unix_name_t device; @@ -155,6 +166,8 @@ enum bluetoothapis_funcs unix_bluetooth_gatt_service_free, unix_bluetooth_gatt_characteristic_free, + unix_bluetooth_gatt_characteristic_value_move, + unix_bluetooth_gatt_characteristic_value_free, unix_bluetooth_get_event, diff --git a/dlls/winebth.sys/unixlib_priv.h b/dlls/winebth.sys/unixlib_priv.h index 3d9fc34a585..d920e7fecbf 100644 --- a/dlls/winebth.sys/unixlib_priv.h +++ b/dlls/winebth.sys/unixlib_priv.h @@ -62,5 +62,7 @@ extern NTSTATUS bluez_auth_agent_send_response( void *auth_agent, struct unix_na extern NTSTATUS bluez_device_disconnect( void *connection, const char *device_path ); extern NTSTATUS bluez_device_start_pairing( void *dbus_connection, void *watcher_ctx, struct unix_name *device, IRP *irp ); extern NTSTATUS bluez_watcher_init( void *connection, void **ctx ); +extern void bluez_gatt_characteristic_value_move( struct winebluetooth_gatt_characteristic_value *value, BYTE *buf ); +extern void bluez_gatt_characteristic_value_free( void *val ); 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 3da8d76a8fb..4a89d871564 100644 --- a/dlls/winebth.sys/winebluetooth.c +++ b/dlls/winebth.sys/winebluetooth.c @@ -1,8 +1,7 @@ /* * Wine bluetooth APIs * - * Copyright 2024 Vibhav Pant - * Copyright 2025 Vibhav Pant + * Copyright 2024-2026 Vibhav Pant * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -225,6 +224,41 @@ void winebluetooth_gatt_characteristic_free( winebluetooth_gatt_characteristic_t UNIX_BLUETOOTH_CALL( bluetooth_gatt_characteristic_free, &args ); } +static const char * +debugstr_winebluetooth_gatt_characteristic_value( const struct winebluetooth_gatt_characteristic_value *val ) +{ + return val ? wine_dbg_sprintf( "{ %I32u { %p } }", val->size, (void *)val->handle ) : "(null)"; +} + +void winebluetooth_gatt_characteristic_value_move( struct winebluetooth_gatt_characteristic_value *val, BYTE *dest ) +{ + struct bluetooth_gatt_characteristic_value_move_params args = {0}; + + TRACE( "(%s, %p)\n", debugstr_winebluetooth_gatt_characteristic_value( val ), dest ); + + if (!winebluetooth_gatt_characteristic_value_is_inline( val )) + { + args.val = val; + args.buf = dest; + UNIX_BLUETOOTH_CALL( bluetooth_gatt_characteristic_value_move, &args ); + } + else if (val->size) + memcpy( dest, val->buf, val->size ); +} + +void winebluetooth_gatt_characteristic_value_free( struct winebluetooth_gatt_characteristic_value *val ) +{ + struct bluetooth_gatt_characteristic_value_free_params args = {0}; + + TRACE( "(%s)\n", debugstr_winebluetooth_gatt_characteristic_value( val ) ); + + if (!winebluetooth_gatt_characteristic_value_is_inline( val )) + { + args.val = val; + UNIX_BLUETOOTH_CALL( bluetooth_gatt_characteristic_value_free, &args ); + } +} + NTSTATUS winebluetooth_get_event( struct winebluetooth_event *result ) { struct bluetooth_get_event_params params = {0}; diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index 56e6296bf60..84ed2b91256 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -131,6 +131,7 @@ struct bluetooth_gatt_characteristic winebluetooth_gatt_characteristic_t characteristic; BTH_LE_GATT_CHARACTERISTIC props; + BTH_LE_GATT_CHARACTERISTIC_VALUE *value; }; enum bluetooth_pdo_ext_type @@ -1334,6 +1335,18 @@ bluetooth_gatt_service_add_characteristic( struct winebluetooth_watcher_event_ga LeaveCriticalSection( &device->props_cs ); goto failed; } + if (characteristic.value.size) + { + entry->value = calloc( 1, offsetof( BTH_LE_GATT_CHARACTERISTIC_VALUE, Data[characteristic.value.size] ) ); + if (!entry->value) + { + LeaveCriticalSection( &device->props_cs ); + free( entry ); + goto failed; + } + entry->value->DataSize = characteristic.value.size; + winebluetooth_gatt_characteristic_value_move( &characteristic.value, entry->value->Data ); + } TRACE( "Adding GATT characteristic %#x under service %s for device %p\n", characteristic.props.AttributeHandle, debugstr_guid( &svc->uuid ), @@ -1353,6 +1366,7 @@ bluetooth_gatt_service_add_characteristic( struct winebluetooth_watcher_event_ga } failed: LeaveCriticalSection( &device_list_cs ); + winebluetooth_gatt_characteristic_value_free( &characteristic.value ); winebluetooth_gatt_characteristic_free( characteristic.characteristic ); winebluetooth_gatt_service_free( characteristic.service ); } @@ -1391,6 +1405,8 @@ static void bluetooth_gatt_characteristic_remove( winebluetooth_gatt_characteris winebluetooth_gatt_characteristic_free( chrc->characteristic ); winebluetooth_gatt_characteristic_free( handle ); + if (chrc->value) + free( chrc->value ); free( chrc ); return; } diff --git a/dlls/winebth.sys/winebth_priv.h b/dlls/winebth.sys/winebth_priv.h index 765fe1d55f7..5b13f78aa59 100644 --- a/dlls/winebth.sys/winebth_priv.h +++ b/dlls/winebth.sys/winebth_priv.h @@ -262,6 +262,26 @@ static inline BOOL winebluetooth_gatt_characteristic_equal( winebluetooth_gatt_c return c1.handle == c2.handle; } +#define WINEBLUETOOTH_CHARACTERISTIC_INLINE_SIZE 256 + +struct winebluetooth_gatt_characteristic_value +{ + UINT32 size; + /* If size <= WINEBLUETOOTH_CHARACTERISTIC_INLINE_SIZE, then the value is stored in buf. + * Otherwise, the Unix handle gets used to perform a copy through the unixlib. */ + union { + BYTE buf[WINEBLUETOOTH_CHARACTERISTIC_INLINE_SIZE]; + UINT_PTR handle; + }; +}; + +void winebluetooth_gatt_characteristic_value_move( struct winebluetooth_gatt_characteristic_value *val, BYTE *dest ); +void winebluetooth_gatt_characteristic_value_free( struct winebluetooth_gatt_characteristic_value *val ); +static inline BOOL winebluetooth_gatt_characteristic_value_is_inline( const struct winebluetooth_gatt_characteristic_value *val ) +{ + return val->size <= ARRAY_SIZE( val->buf ); +} + enum winebluetooth_watcher_event_type { BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED, @@ -346,6 +366,7 @@ struct winebluetooth_watcher_event_gatt_characteristic_added winebluetooth_gatt_characteristic_t characteristic; winebluetooth_gatt_service_t service; BTH_LE_GATT_CHARACTERISTIC props; + struct winebluetooth_gatt_characteristic_value value; }; union winebluetooth_watcher_event_data diff --git a/include/bthledef.h b/include/bthledef.h index 32585c06f8e..3cc9e01410a 100644 --- a/include/bthledef.h +++ b/include/bthledef.h @@ -54,6 +54,12 @@ typedef struct _BTH_LE_GATT_CHARACTERISTIC BOOLEAN HasExtendedProperties; } BTH_LE_GATT_CHARACTERISTIC, *PBTH_LE_GATT_CHARACTERISTIC; +typedef struct _BTH_LE_GATT_CHARACTERISTIC_VALUE +{ + ULONG DataSize; + UCHAR Data[1]; +} BTH_LE_GATT_CHARACTERISTIC_VALUE, *PBTH_LE_GATT_CHARACTERISTIC_VALUE; + DEFINE_GUID( GUID_BLUETOOTHLE_DEVICE_INTERFACE, 0x781aee18, 0x7733, 0x4ce4, 0xad, 0xd0, 0x91, 0xf4, 0x1c, 0x67, 0xb5, 0x92 ); DEFINE_GUID( GUID_BLUETOOTH_GATT_SERVICE_DEVICE_INTERFACE, 0x6e3bb679, 0x4372, 0x40c8, 0x9e, 0xaa, 0x45, 0x09, 0xdf, 0x26, 0x0c, 0xd8 ); DEFINE_GUID( BTH_LE_ATT_BLUETOOTH_BASE_GUID, 0, 0, 0x1000, 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10208