-- v8: winebth.sys: Set additional properties for remote Bluetooth devices. bluetoothapis/tests: Implement tests for BluetoothGATTGetCharacteristics. bluetoothapis: Implement BluetoothGATTGetCharacteristics.
From: Vibhav Pant vibhavp@gmail.com
--- dlls/winebth.sys/dbus.c | 19 ++++++++++++++ dlls/winebth.sys/winebth.c | 45 +++++++++++++++++++++++++++++++++ dlls/winebth.sys/winebth_priv.h | 6 +++++ 3 files changed, 70 insertions(+)
diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index 08d63ceb307..5f736583f79 100644 --- a/dlls/winebth.sys/dbus.c +++ b/dlls/winebth.sys/dbus.c @@ -1677,6 +1677,22 @@ static DBusHandlerResult bluez_filter( DBusConnection *conn, DBusMessage *msg, v event )) unix_name_free( device ); } + else if (!strcmp( interfaces[i], BLUEZ_INTERFACE_GATT_SERVICE )) + { + struct unix_name *service; + union winebluetooth_watcher_event_data event; + + service = unix_name_get_or_create( object_path ); + if (!service) + { + ERR( "Failed to allocate memory for GATT service path %s\n", debugstr_a( object_path ) ); + continue; + } + event.gatt_service_removed.handle = (UINT_PTR)service; + if (!bluez_event_list_queue_new_event( event_list, BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_GATT_SERVICE_REMOVED, + event )) + unix_name_free( service ); + } } p_dbus_free_string_array( interfaces ); } @@ -1918,6 +1934,9 @@ static void bluez_watcher_free( struct bluez_watcher_ctx *watcher ) unix_name_free( (struct unix_name *)event1->event.gatt_service_added.device.handle ); unix_name_free( (struct unix_name *)event1->event.gatt_service_added.service.handle ); break; + case BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_GATT_SERVICE_REMOVED: + unix_name_free( (struct unix_name *)event1->event.gatt_service_removed.handle ); + break; } free( event1 ); } diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index bc880019740..637a080e470 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -1080,6 +1080,48 @@ static void bluetooth_device_add_gatt_service( struct winebluetooth_watcher_even winebluetooth_gatt_service_free( event.service ); }
+static void bluetooth_gatt_service_remove( winebluetooth_gatt_service_t service ) +{ + struct bluetooth_radio *radio; + + EnterCriticalSection( &device_list_cs ); + LIST_FOR_EACH_ENTRY( radio, &device_list, struct bluetooth_radio, entry ) + { + struct bluetooth_remote_device *device; + + EnterCriticalSection( &radio->remote_devices_cs ); + LIST_FOR_EACH_ENTRY( device, &radio->remote_devices, struct bluetooth_remote_device, entry ) + { + struct bluetooth_gatt_service *svc; + + EnterCriticalSection( &device->props_cs ); + if (!device->le) + { + LeaveCriticalSection( &device->props_cs ); + continue; + } + LIST_FOR_EACH_ENTRY( svc, &device->gatt_services, struct bluetooth_gatt_service, entry ) + { + if (winebluetooth_gatt_service_equal( svc->service, service )) + { + list_remove( &svc->entry ); + LeaveCriticalSection( &device->props_cs ); + LeaveCriticalSection( &radio->remote_devices_cs ); + LeaveCriticalSection( &device_list_cs ); + winebluetooth_gatt_service_free( svc->service ); + free( svc ); + winebluetooth_gatt_service_free( service ); + return; + } + } + LeaveCriticalSection( &device->props_cs ); + } + LeaveCriticalSection( &radio->remote_devices_cs ); + } + LeaveCriticalSection( &device_list_cs ); + winebluetooth_gatt_service_free( service ); +} + static DWORD CALLBACK bluetooth_event_loop_thread_proc( void *arg ) { NTSTATUS status; @@ -1122,6 +1164,9 @@ static DWORD CALLBACK bluetooth_event_loop_thread_proc( void *arg ) case BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_GATT_SERVICE_ADDED: bluetooth_device_add_gatt_service( event->event_data.gatt_service_added ); break; + case BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_GATT_SERVICE_REMOVED: + bluetooth_gatt_service_remove( event->event_data.gatt_service_removed ); + break; default: FIXME( "Unknown bluetooth watcher event code: %#x\n", event->event_type ); } diff --git a/dlls/winebth.sys/winebth_priv.h b/dlls/winebth.sys/winebth_priv.h index afacccdfb2c..6ba90f36615 100644 --- a/dlls/winebth.sys/winebth_priv.h +++ b/dlls/winebth.sys/winebth_priv.h @@ -222,6 +222,10 @@ NTSTATUS winebluetooth_auth_send_response( winebluetooth_device_t device, BLUETO NTSTATUS winebluetooth_device_start_pairing( winebluetooth_device_t device, IRP *irp );
void winebluetooth_gatt_service_free( winebluetooth_gatt_service_t service ); +static inline BOOL winebluetooth_gatt_service_equal( winebluetooth_gatt_service_t s1, winebluetooth_gatt_service_t s2) +{ + return s1.handle == s2.handle; +}
enum winebluetooth_watcher_event_type { @@ -233,6 +237,7 @@ enum winebluetooth_watcher_event_type BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_PROPERTIES_CHANGED, BLUETOOTH_WATCHER_EVENT_TYPE_PAIRING_FINISHED, BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_GATT_SERVICE_ADDED, + BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_GATT_SERVICE_REMOVED, };
struct winebluetooth_watcher_event_radio_added @@ -300,6 +305,7 @@ union winebluetooth_watcher_event_data struct winebluetooth_watcher_event_device_props_changed device_props_changed; struct winebluetooth_watcher_event_pairing_finished pairing_finished; struct winebluetooth_watcher_event_gatt_service_added gatt_service_added; + winebluetooth_gatt_service_t gatt_service_removed; };
struct winebluetooth_watcher_event
From: Vibhav Pant vibhavp@gmail.com
--- dlls/winebth.sys/dbus.c | 127 ++++++++++++++++++++++++++++++- dlls/winebth.sys/unixlib.c | 10 +++ dlls/winebth.sys/unixlib.h | 7 ++ dlls/winebth.sys/winebluetooth.c | 10 +++ dlls/winebth.sys/winebth.c | 88 +++++++++++++++++---- dlls/winebth.sys/winebth_priv.h | 46 +++++++++++ include/bthledef.h | 16 ++++ 7 files changed, 284 insertions(+), 20 deletions(-)
diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index 5f736583f79..c014654390a 100644 --- a/dlls/winebth.sys/dbus.c +++ b/dlls/winebth.sys/dbus.c @@ -121,6 +121,7 @@ const int bluez_timeout = -1; #define BLUEZ_INTERFACE_AGENT_MANAGER "org.bluez.AgentManager1" #define BLUEZ_INTERFACE_AGENT "org.bluez.Agent1" #define BLUEZ_INTERFACE_GATT_SERVICE "org.bluez.GattService1" +#define BLUEZ_INTERFACE_GATT_CHARACTERISTICS "org.bluez.GattCharacteristic1"
#define DO_FUNC( f ) typeof( f ) (*p_##f) DBUS_FUNCS; @@ -857,6 +858,8 @@ struct bluez_watcher_ctx struct list initial_device_list; /* struct bluez_init_entry */ struct list initial_gatt_service_list; + /* struct bluez_init_entry */ + struct list initial_gatt_chars_list;
/* struct bluez_watcher_event */ struct list event_list; @@ -868,6 +871,7 @@ struct bluez_init_entry struct winebluetooth_watcher_event_radio_added radio; struct winebluetooth_watcher_event_device_added device; struct winebluetooth_watcher_event_gatt_service_added service; + struct winebluetooth_watcher_event_gatt_characteristic_added characteristic; } object; struct list entry; }; @@ -1937,6 +1941,10 @@ static void bluez_watcher_free( struct bluez_watcher_ctx *watcher ) case BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_GATT_SERVICE_REMOVED: unix_name_free( (struct unix_name *)event1->event.gatt_service_removed.handle ); break; + 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 ); + break; } free( event1 ); } @@ -1965,6 +1973,7 @@ NTSTATUS bluez_watcher_init( void *connection, void **ctx ) list_init( &watcher_ctx->initial_radio_list ); list_init( &watcher_ctx->initial_device_list ); list_init( &watcher_ctx->initial_gatt_service_list ); + list_init( &watcher_ctx->initial_gatt_chars_list ); list_init( &watcher_ctx->event_list );
/* The bluez_dbus_loop thread will free up the watcher when the disconnect message is processed (i.e, @@ -2020,7 +2029,8 @@ void bluez_watcher_close( void *connection, void *ctx ) }
static NTSTATUS bluez_build_initial_device_lists( DBusMessage *reply, struct list *adapter_list, - struct list *device_list, struct list *gatt_service_list ) + struct list *device_list, struct list *gatt_service_list, + struct list *gatt_chars_list ) { DBusMessageIter dict, paths_iter, iface_iter, prop_iter; const char *path; @@ -2199,11 +2209,109 @@ static NTSTATUS bluez_build_initial_device_lists( DBusMessage *reply, struct lis TRACE( "Found BlueZ org.bluez.GattService1 object %s %p\n", debugstr_a( path ), service_name ); break; } + else if (!strcmp( iface, BLUEZ_INTERFACE_GATT_CHARACTERISTICS )) + { + struct unix_name *service_name = NULL, *char_name; + struct bluez_init_entry *init_entry; + BTH_LE_GATT_CHARACTERISTIC *props; + DBusMessageIter variant; + const char *prop_name; + + init_entry = calloc( 1, sizeof( *init_entry ) ); + if (!init_entry) + { + status = STATUS_NO_MEMORY; + goto done; + } + char_name = unix_name_get_or_create( path ); + if (!char_name) + { + ERR("Failed to allocate memory for characteristic path %s\n", debugstr_a( path )); + free( init_entry ); + goto done; + } + + props = &init_entry->object.characteristic.props; + init_entry->object.characteristic.characteristic.handle = (UINT_PTR)char_name; + while ((prop_name = bluez_next_dict_entry( &prop_iter, &variant ))) + { + if (!strcmp( prop_name, "Flags" ) + && p_dbus_message_iter_get_arg_type ( &variant ) == DBUS_TYPE_ARRAY + && p_dbus_message_iter_get_element_type ( &variant ) == DBUS_TYPE_STRING) + { + DBusMessageIter flags_iter; + const struct { + const char *name; + BOOLEAN *flag; + } flags[] = { + { "broadcast", &props->IsBroadcastable }, + { "read", &props->IsReadable }, + { "write", &props->IsWritable }, + { "write-without-response", &props->IsWritableWithoutResponse }, + { "authenticate-signed-writes", &props->IsSignedWritable }, + { "notify", &props->IsNotifiable }, + { "indicate", &props->IsIndicatable }, + { "extended-properties", &props->HasExtendedProperties }, + }; + + p_dbus_message_iter_recurse( &variant, &flags_iter ); + while (p_dbus_message_iter_get_arg_type( &flags_iter ) != DBUS_TYPE_INVALID) + { + const char *flag_name; + SIZE_T i; + + p_dbus_message_iter_get_basic( &flags_iter, &flag_name ); + for (i = 0; i < ARRAY_SIZE( flags ); i++) + { + if (!strcmp( flags[i].name, flag_name )) + *flags[i].flag = TRUE; + } + p_dbus_message_iter_next( &flags_iter ); + } + } + else if (!strcmp( prop_name, "Service" ) + && p_dbus_message_iter_get_arg_type( &variant ) == DBUS_TYPE_OBJECT_PATH) + { + const char *path; + + p_dbus_message_iter_get_basic( &variant, &path ); + service_name = unix_name_get_or_create( path ); + } + else if (!strcmp( prop_name, "UUID" ) + && p_dbus_message_iter_get_arg_type( &variant ) == DBUS_TYPE_STRING) + { + const char *uuid_str; + GUID uuid; + + p_dbus_message_iter_get_basic( &variant, &uuid_str ); + if (parse_uuid( &uuid, uuid_str )) + uuid_to_le( &uuid, &props->CharacteristicUuid ); + else + ERR( "Failed to parse UUID %s for GATT characteristic %s\n", debugstr_a( uuid_str ), path ); + } + else if (!strcmp( prop_name, "Handle" ) + && p_dbus_message_iter_get_arg_type( &variant ) == DBUS_TYPE_UINT16) + p_dbus_message_iter_get_basic( &variant, &props->AttributeHandle ); + } + if (!service_name) + { + unix_name_free( char_name ); + free( init_entry ); + ERR( "Could not find the associated service for the GATT charcteristic %s\n", debugstr_a( path ) ); + break; + } + init_entry->object.characteristic.service.handle = (UINT_PTR)service_name; + + list_add_tail( gatt_chars_list, &init_entry->entry ); + TRACE( "Found Bluez org.bluez.GattCharacteristic1 object %s %p\n", debugstr_a( path ), char_name ); + break; + } } }
- TRACE( "Initial device list: radios: %d, devices: %d, GATT services: %d\n", list_count( adapter_list ), - list_count( device_list ), list_count( gatt_service_list ) ); + TRACE( "Initial device list: radios: %d, devices: %d, GATT services: %d, characteristics: %d \n", + list_count( adapter_list ), list_count( device_list ), list_count( gatt_service_list ), + list_count( gatt_chars_list ) ); done: return status; } @@ -2243,6 +2351,16 @@ static BOOL bluez_watcher_event_queue_ready( struct bluez_watcher_ctx *ctx, stru free( service ); return TRUE; } + if (!list_empty( &ctx->initial_gatt_chars_list )) + { + struct bluez_init_entry *characteristic; + characteristic = LIST_ENTRY( list_head( &ctx->initial_gatt_chars_list ), struct bluez_init_entry, entry ); + event->event_type = BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_ADDED; + event->event_data.gatt_characteristic_added = characteristic->object.characteristic; + list_remove( &characteristic->entry ); + free( characteristic ); + return TRUE; + } if (!list_empty( &ctx->event_list )) { struct bluez_watcher_event *watcher_event = @@ -2340,7 +2458,8 @@ NTSTATUS bluez_dbus_loop( void *c, void *watcher, void *auth_agent, } status = bluez_build_initial_device_lists( reply, &watcher_ctx->initial_radio_list, &watcher_ctx->initial_device_list, - &watcher_ctx->initial_gatt_service_list ); + &watcher_ctx->initial_gatt_service_list, + &watcher_ctx->initial_gatt_chars_list ); p_dbus_message_unref( reply ); if (status != STATUS_SUCCESS) { diff --git a/dlls/winebth.sys/unixlib.c b/dlls/winebth.sys/unixlib.c index 5e020464ca4..763f2f32346 100644 --- a/dlls/winebth.sys/unixlib.c +++ b/dlls/winebth.sys/unixlib.c @@ -255,6 +255,14 @@ static NTSTATUS bluetooth_gatt_service_free( void *args ) return STATUS_SUCCESS; }
+static NTSTATUS bluetooth_gatt_characteristic_free( void *args ) +{ + struct bluetooth_gatt_characteristic_free_params *params = args; + unix_name_free( params->characteristic ); + return STATUS_SUCCESS; +} + + static NTSTATUS bluetooth_get_event( void *args ) { struct bluetooth_get_event_params *params = args; @@ -284,6 +292,8 @@ const unixlib_entry_t __wine_unix_call_funcs[] = {
bluetooth_gatt_service_free,
+ bluetooth_gatt_characteristic_free, + bluetooth_get_event, };
diff --git a/dlls/winebth.sys/unixlib.h b/dlls/winebth.sys/unixlib.h index 42783ccf400..4629a7191d4 100644 --- a/dlls/winebth.sys/unixlib.h +++ b/dlls/winebth.sys/unixlib.h @@ -59,6 +59,11 @@ struct bluetooth_gatt_service_free_params unix_name_t service; };
+struct bluetooth_gatt_characteristic_free_params +{ + unix_name_t characteristic; +}; + struct bluetooth_device_disconnect_params { unix_name_t device; @@ -137,6 +142,8 @@ enum bluetoothapis_funcs
unix_bluetooth_gatt_service_free,
+ unix_bluetooth_gatt_characteristic_free, + unix_bluetooth_get_event,
unix_funcs_count diff --git a/dlls/winebth.sys/winebluetooth.c b/dlls/winebth.sys/winebluetooth.c index add7020a0b3..7762b0f3fd1 100644 --- a/dlls/winebth.sys/winebluetooth.c +++ b/dlls/winebth.sys/winebluetooth.c @@ -198,6 +198,16 @@ void winebluetooth_gatt_service_free( winebluetooth_gatt_service_t service ) UNIX_BLUETOOTH_CALL( bluetooth_gatt_service_free, &args ); }
+void winebluetooth_gatt_characteristic_free( winebluetooth_gatt_characteristic_t characteristic ) +{ + struct bluetooth_gatt_characteristic_free_params args = {0}; + + TRACE( "(%p)\n", (void *)characteristic.handle ); + + args.characteristic = characteristic.handle; + UNIX_BLUETOOTH_CALL( bluetooth_gatt_characteristic_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 637a080e470..c9151b49f19 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -115,6 +115,16 @@ struct bluetooth_gatt_service GUID uuid; unsigned int primary : 1; UINT16 handle; + + struct list characteristics; +}; + +struct bluetooth_gatt_characteristic +{ + struct list entry; + + winebluetooth_gatt_characteristic_t characteristic; + BTH_LE_GATT_CHARACTERISTIC props; };
enum bluetooth_pdo_ext_type @@ -154,22 +164,6 @@ static NTSTATUS WINAPI dispatch_auth( DEVICE_OBJECT *device, IRP *irp ) return status; }
-static void uuid_to_le( const GUID *uuid, BTH_LE_UUID *le_uuid ) -{ - if (uuid->Data1 <= UINT16_MAX && uuid->Data2 == BTH_LE_ATT_BLUETOOTH_BASE_GUID.Data2 - && uuid->Data3 == BTH_LE_ATT_BLUETOOTH_BASE_GUID.Data3 - && !memcmp( uuid->Data4, BTH_LE_ATT_BLUETOOTH_BASE_GUID.Data4, sizeof( uuid->Data4 ) )) - { - le_uuid->IsShortUuid = TRUE; - le_uuid->Value.ShortUuid = uuid->Data1; - } - else - { - le_uuid->IsShortUuid = FALSE; - le_uuid->Value.LongUuid = *uuid; - } -} - static NTSTATUS bluetooth_remote_device_dispatch( DEVICE_OBJECT *device, struct bluetooth_remote_device *ext, IRP *irp ) { IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp ); @@ -1062,6 +1056,7 @@ static void bluetooth_device_add_gatt_service( struct winebluetooth_watcher_even service->primary = !!event.is_primary; service->handle = event.attr_handle; bluetooth_device_enable_le_iface( device ); + list_init( &service->characteristics );
EnterCriticalSection( &device->props_cs ); list_add_tail( &device->gatt_services, &service->entry ); @@ -1122,6 +1117,64 @@ static void bluetooth_gatt_service_remove( winebluetooth_gatt_service_t service winebluetooth_gatt_service_free( service ); }
+static void +bluetooth_gatt_service_add_characteristic( struct winebluetooth_watcher_event_gatt_characteristic_added characteristic ) +{ + struct bluetooth_radio *radio; + + EnterCriticalSection( &device_list_cs ); + LIST_FOR_EACH_ENTRY( radio, &device_list, struct bluetooth_radio, entry ) + { + struct bluetooth_remote_device *device; + + EnterCriticalSection( &radio->remote_devices_cs ); + LIST_FOR_EACH_ENTRY( device, &radio->remote_devices, struct bluetooth_remote_device, entry ) + { + struct bluetooth_gatt_service *svc; + + EnterCriticalSection( &device->props_cs ); + if (!device->le) + { + LeaveCriticalSection( &device->props_cs ); + continue; + } + LIST_FOR_EACH_ENTRY( svc, &device->gatt_services, struct bluetooth_gatt_service, entry ) + { + if (winebluetooth_gatt_service_equal( svc->service, characteristic.service )) + { + struct bluetooth_gatt_characteristic *entry; + + if (!(entry = calloc( 1, sizeof( *entry ) ))) + { + LeaveCriticalSection( &device->props_cs ); + LeaveCriticalSection( &radio->remote_devices_cs ); + goto failed; + } + + TRACE( "Adding GATT characteristic %#x under service %s for device %p\n", + characteristic.props.AttributeHandle, debugstr_guid( &svc->uuid ), + (void *)device->device.handle ); + + entry->characteristic = characteristic.characteristic; + entry->props = characteristic.props; + list_add_tail( &svc->characteristics, &entry->entry ); + LeaveCriticalSection( &device->props_cs ); + LeaveCriticalSection( &radio->remote_devices_cs ); + LeaveCriticalSection( &device_list_cs ); + winebluetooth_gatt_service_free( characteristic.service ); + return; + } + } + LeaveCriticalSection( &device->props_cs ); + } + LeaveCriticalSection( &radio->remote_devices_cs ); + } +failed: + LeaveCriticalSection( &device_list_cs ); + winebluetooth_gatt_characteristic_free( characteristic.characteristic ); + winebluetooth_gatt_service_free( characteristic.service ); +} + static DWORD CALLBACK bluetooth_event_loop_thread_proc( void *arg ) { NTSTATUS status; @@ -1167,6 +1220,9 @@ static DWORD CALLBACK bluetooth_event_loop_thread_proc( void *arg ) case BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_GATT_SERVICE_REMOVED: bluetooth_gatt_service_remove( event->event_data.gatt_service_removed ); break; + case BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_ADDED: + bluetooth_gatt_service_add_characteristic( event->event_data.gatt_characteristic_added ); + break; default: FIXME( "Unknown bluetooth watcher event code: %#x\n", event->event_type ); } diff --git a/dlls/winebth.sys/winebth_priv.h b/dlls/winebth.sys/winebth_priv.h index 6ba90f36615..e21b3b586d7 100644 --- a/dlls/winebth.sys/winebth_priv.h +++ b/dlls/winebth.sys/winebth_priv.h @@ -22,7 +22,10 @@ #ifndef __WINE_WINEBTH_WINEBTH_H_ #define __WINE_WINEBTH_WINEBTH_H_
+#include <stdint.h> + #include <bthsdpdef.h> +#include <bthledef.h> #include <bluetoothapis.h> #include <bthdef.h> #include <ddk/wdm.h> @@ -97,6 +100,24 @@ static inline const char *debugstr_minor_function_code( UCHAR code ) } #undef XX
+static inline void uuid_to_le( const GUID *uuid, BTH_LE_UUID *le_uuid ) +{ + const GUID le_base_uuid = { 0, 0, 0x1000, { 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb } }; + + if (uuid->Data1 <= UINT16_MAX && uuid->Data2 == le_base_uuid.Data2 + && uuid->Data3 == le_base_uuid.Data3 + && !memcmp( uuid->Data4, le_base_uuid.Data4, sizeof( uuid->Data4 ) )) + { + le_uuid->IsShortUuid = TRUE; + le_uuid->Value.ShortUuid = uuid->Data1; + } + else + { + le_uuid->IsShortUuid = FALSE; + le_uuid->Value.LongUuid = *uuid; + } +} + /* Undocumented device properties for Bluetooth radios. */ #define DEFINE_BTH_RADIO_DEVPROPKEY( d, i ) \ DEFINE_DEVPROPKEY( DEVPKEY_BluetoothRadio_##d, 0xa92f26ca, 0xeda7, 0x4b1d, 0x9d, 0xb2, 0x27, \ @@ -192,6 +213,11 @@ typedef struct UINT_PTR handle; } winebluetooth_gatt_service_t;
+typedef struct +{ + UINT_PTR handle; +} winebluetooth_gatt_characteristic_t; + NTSTATUS winebluetooth_radio_get_unique_name( winebluetooth_radio_t radio, char *name, SIZE_T *size ); void winebluetooth_radio_free( winebluetooth_radio_t radio ); @@ -227,6 +253,8 @@ static inline BOOL winebluetooth_gatt_service_equal( winebluetooth_gatt_service_ return s1.handle == s2.handle; }
+void winebluetooth_gatt_characteristic_free( winebluetooth_gatt_characteristic_t characteristic ); + enum winebluetooth_watcher_event_type { BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED, @@ -238,6 +266,7 @@ enum winebluetooth_watcher_event_type BLUETOOTH_WATCHER_EVENT_TYPE_PAIRING_FINISHED, BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_GATT_SERVICE_ADDED, BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_GATT_SERVICE_REMOVED, + BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_ADDED, };
struct winebluetooth_watcher_event_radio_added @@ -295,6 +324,22 @@ struct winebluetooth_watcher_event_gatt_service_added GUID uuid; };
+#define WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_BROADCAST 1 +#define WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_READ (1 << 1) +#define WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_WRITE (1 << 2) +#define WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_NOTIFY (1 << 3) +#define WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_INDICATE (1 << 4) +#define WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_WRITE_SIGNED (1 << 5) +#define WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_EXTENDED_PROPS (1 << 6) +#define WINEBLUETOOTH_GATT_CHARACTERISTIC_FLAGS_WRITE_WITHOUT_RESPONSE (1 << 7) + +struct winebluetooth_watcher_event_gatt_characteristic_added +{ + winebluetooth_gatt_characteristic_t characteristic; + winebluetooth_gatt_service_t service; + BTH_LE_GATT_CHARACTERISTIC props; +}; + union winebluetooth_watcher_event_data { struct winebluetooth_watcher_event_radio_added radio_added; @@ -306,6 +351,7 @@ union winebluetooth_watcher_event_data struct winebluetooth_watcher_event_pairing_finished pairing_finished; struct winebluetooth_watcher_event_gatt_service_added gatt_service_added; winebluetooth_gatt_service_t gatt_service_removed; + struct winebluetooth_watcher_event_gatt_characteristic_added gatt_characteristic_added; };
struct winebluetooth_watcher_event diff --git a/include/bthledef.h b/include/bthledef.h index d3d0393be99..25bb2055db2 100644 --- a/include/bthledef.h +++ b/include/bthledef.h @@ -34,6 +34,22 @@ typedef struct _BTH_LE_GATT_SERVICE USHORT AttributeHandle; } BTH_LE_GATT_SERVICE, *PBTH_LE_GATT_SERVICE;
+typedef struct _BTH_LE_GATT_CHARACTERISTIC +{ + USHORT ServiceHandle; + BTH_LE_UUID CharacteristicUuid; + USHORT AttributeHandle; + USHORT CharacteristicValueHandle; + BOOLEAN IsBroadcastable; + BOOLEAN IsReadable; + BOOLEAN IsWritable; + BOOLEAN IsWritableWithoutResponse; + BOOLEAN IsSignedWritable; + BOOLEAN IsNotifiable; + BOOLEAN IsIndicatable; + BOOLEAN HasExtendedProperties; +} BTH_LE_GATT_CHARACTERISTIC, *PBTH_LE_GATT_CHARACTERISTIC; + DEFINE_GUID( GUID_BLUETOOTHLE_DEVICE_INTERFACE, 0x781aee18, 0x7733, 0x4ce4, 0xad, 0xd0, 0x91, 0xf4, 0x1c, 0x67, 0xb5, 0x92 ); DEFINE_GUID( BTH_LE_ATT_BLUETOOTH_BASE_GUID, 0, 0, 0x1000, 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb ); #endif
From: Vibhav Pant vibhavp@gmail.com
--- dlls/winebth.sys/winebth.c | 84 +++++++++++++++++++++++++++++++++++++- include/wine/winebth.h | 9 ++++ 2 files changed, 92 insertions(+), 1 deletion(-)
diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index c9151b49f19..a4f68cfa37c 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -116,7 +116,8 @@ struct bluetooth_gatt_service unsigned int primary : 1; UINT16 handle;
- struct list characteristics; + CRITICAL_SECTION chars_cs; + struct list characteristics; /* Guarded by chars_cs */ };
struct bluetooth_gatt_characteristic @@ -164,6 +165,29 @@ static NTSTATUS WINAPI dispatch_auth( DEVICE_OBJECT *device, IRP *irp ) return status; }
+static void le_to_uuid( const BTH_LE_UUID *le_uuid, GUID *uuid ) +{ + if (le_uuid->IsShortUuid) + { + *uuid = BTH_LE_ATT_BLUETOOTH_BASE_GUID; + uuid->Data1 = le_uuid->Value.ShortUuid; + } + else + *uuid = le_uuid->Value.LongUuid; +} + +/* Caller should hold props_cs */ +static struct bluetooth_gatt_service *find_gatt_service( struct list *services, const GUID *uuid, UINT16 handle ) +{ + struct bluetooth_gatt_service *service; + LIST_FOR_EACH_ENTRY( service, services, struct bluetooth_gatt_service, entry ) + { + if (IsEqualGUID( &service->uuid, uuid ) && service->handle == handle) + return service; + } + return NULL; +} + static NTSTATUS bluetooth_remote_device_dispatch( DEVICE_OBJECT *device, struct bluetooth_remote_device *ext, IRP *irp ) { IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp ); @@ -216,6 +240,53 @@ static NTSTATUS bluetooth_remote_device_dispatch( DEVICE_OBJECT *device, struct status = STATUS_MORE_ENTRIES; break; } + case IOCTL_WINEBTH_LE_DEVICE_GET_GATT_CHARACTERISTICS: + { + const SIZE_T min_size = offsetof( struct winebth_le_device_get_gatt_characteristics_params, characteristics[0] ); + struct winebth_le_device_get_gatt_characteristics_params *chars = irp->AssociatedIrp.SystemBuffer; + struct bluetooth_gatt_characteristic *chrc; + struct bluetooth_gatt_service *service; + SIZE_T rem; + GUID uuid; + + if (!chars || outsize < min_size) + { + status = STATUS_INVALID_USER_BUFFER; + break; + } + + rem = (outsize - min_size)/sizeof( *chars->characteristics ); + status = STATUS_SUCCESS; + chars->count = 0; + le_to_uuid( &chars->service.ServiceUuid, &uuid ); + + EnterCriticalSection( &ext->props_cs ); + service = find_gatt_service( &ext->gatt_services, &uuid, chars->service.AttributeHandle ); + if (!service) + { + status = STATUS_INVALID_PARAMETER; + LeaveCriticalSection( &ext->props_cs ); + break; + } + + EnterCriticalSection( &service->chars_cs ); + LIST_FOR_EACH_ENTRY( chrc, &service->characteristics, struct bluetooth_gatt_characteristic, entry ) + { + chars->count++; + if (rem) + { + chars->characteristics[chars->count - 1] = chrc->props; + rem--; + } + } + LeaveCriticalSection( &service->chars_cs ); + LeaveCriticalSection( &ext->props_cs ); + + irp->IoStatus.Information = offsetof( struct winebth_le_device_get_gatt_characteristics_params, characteristics[chars->count] ); + if (chars->count > rem) + status = STATUS_MORE_ENTRIES; + break; + } default: FIXME( "Unimplemented IOCTL code: %#lx\n", code ); } @@ -1057,6 +1128,8 @@ static void bluetooth_device_add_gatt_service( struct winebluetooth_watcher_even service->handle = event.attr_handle; bluetooth_device_enable_le_iface( device ); list_init( &service->characteristics ); + InitializeCriticalSectionEx( &service->chars_cs, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO ); + service->chars_cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": bluetooth_gatt_service.chars_le");
EnterCriticalSection( &device->props_cs ); list_add_tail( &device->gatt_services, &service->entry ); @@ -1099,11 +1172,20 @@ static void bluetooth_gatt_service_remove( winebluetooth_gatt_service_t service { if (winebluetooth_gatt_service_equal( svc->service, service )) { + struct bluetooth_gatt_characteristic *cur, *next; + list_remove( &svc->entry ); LeaveCriticalSection( &device->props_cs ); LeaveCriticalSection( &radio->remote_devices_cs ); LeaveCriticalSection( &device_list_cs ); winebluetooth_gatt_service_free( svc->service ); + svc->chars_cs.DebugInfo->Spare[0] = 0; + DeleteCriticalSection( &svc->chars_cs ); + LIST_FOR_EACH_ENTRY_SAFE( cur, next, &svc->characteristics, struct bluetooth_gatt_characteristic, entry ) + { + winebluetooth_gatt_characteristic_free( cur->characteristic ); + free( cur ); + } free( svc ); winebluetooth_gatt_service_free( service ); return; diff --git a/include/wine/winebth.h b/include/wine/winebth.h index 6a2893d6474..572be91bb45 100644 --- a/include/wine/winebth.h +++ b/include/wine/winebth.h @@ -38,6 +38,8 @@
/* Get all primary GATT services for the LE device. */ #define IOCTL_WINEBTH_LE_DEVICE_GET_GATT_SERVICES CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xc0, METHOD_BUFFERED, FILE_ANY_ACCESS) +/* Get all characteristics for a GATT service */ +#define IOCTL_WINEBTH_LE_DEVICE_GET_GATT_CHARACTERISTICS CTL_CODE(FILE_DEVICE_BLUETOOTH, 0xc1, METHOD_BUFFERED, FILE_ANY_ACCESS)
DEFINE_GUID( GUID_WINEBTH_AUTHENTICATION_REQUEST, 0xca67235f, 0xf621, 0x4c27, 0x85, 0x65, 0xa4, 0xd5, 0x5e, 0xa1, 0x26, 0xe8 ); @@ -84,6 +86,13 @@ struct winebth_le_device_get_gatt_services_params BTH_LE_GATT_SERVICE services[0]; };
+struct winebth_le_device_get_gatt_characteristics_params +{ + BTH_LE_GATT_SERVICE service; + ULONG count; + BTH_LE_GATT_CHARACTERISTIC characteristics[0]; +}; + #pragma pack(pop)
#endif /* __WINEBTH_H__ */
From: Vibhav Pant vibhavp@gmail.com
--- dlls/winebth.sys/dbus.c | 19 ++++++++++++ dlls/winebth.sys/winebth.c | 53 +++++++++++++++++++++++++++++++++ dlls/winebth.sys/winebth_priv.h | 7 +++++ 3 files changed, 79 insertions(+)
diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index c014654390a..0bc03d24a66 100644 --- a/dlls/winebth.sys/dbus.c +++ b/dlls/winebth.sys/dbus.c @@ -1697,6 +1697,22 @@ static DBusHandlerResult bluez_filter( DBusConnection *conn, DBusMessage *msg, v event )) unix_name_free( service ); } + else if (!strcmp( interfaces[i], BLUEZ_INTERFACE_GATT_CHARACTERISTICS )) + { + struct unix_name *chrc; + union winebluetooth_watcher_event_data event; + + chrc = unix_name_get_or_create( object_path ); + if (!chrc) + { + ERR( "Failed to allocate memory for GATT characteristic path %s\n", debugstr_a( object_path ) ); + continue; + } + event.gatt_characterisic_removed.handle = (UINT_PTR)chrc; + if (!bluez_event_list_queue_new_event( event_list, BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_REMOVED, + event )) + unix_name_free( chrc ); + } } p_dbus_free_string_array( interfaces ); } @@ -1945,6 +1961,9 @@ static void bluez_watcher_free( struct bluez_watcher_ctx *watcher ) 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 ); break; + case BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_REMOVED: + unix_name_free( (struct unix_name *)event1->event.gatt_characterisic_removed.handle ); + break; } free( event1 ); } diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index a4f68cfa37c..e8fe328e18a 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -1257,6 +1257,56 @@ failed: winebluetooth_gatt_service_free( characteristic.service ); }
+static void bluetooth_gatt_characteristic_remove( winebluetooth_gatt_characteristic_t handle ) +{ + struct bluetooth_radio *radio; + + EnterCriticalSection( &device_list_cs ); + LIST_FOR_EACH_ENTRY( radio, &device_list, struct bluetooth_radio, entry ) + { + struct bluetooth_remote_device *device; + + EnterCriticalSection( &radio->remote_devices_cs ); + LIST_FOR_EACH_ENTRY( device, &radio->remote_devices, struct bluetooth_remote_device, entry ) + { + struct bluetooth_gatt_service *svc; + + EnterCriticalSection( &device->props_cs ); + if (!device->le) + { + LeaveCriticalSection( &device->props_cs ); + continue; + } + LIST_FOR_EACH_ENTRY( svc, &device->gatt_services, struct bluetooth_gatt_service, entry ) + { + struct bluetooth_gatt_characteristic *chrc; + EnterCriticalSection( &svc->chars_cs ); + LIST_FOR_EACH_ENTRY( chrc, &svc->characteristics, struct bluetooth_gatt_characteristic, entry ) + { + if (winebluetooth_gatt_characteristic_equal( chrc->characteristic, handle )) + { + list_remove( &chrc->entry ); + LeaveCriticalSection( &svc->chars_cs ); + LeaveCriticalSection( &device->props_cs ); + LeaveCriticalSection( &radio->remote_devices_cs ); + LeaveCriticalSection( &device_list_cs ); + + winebluetooth_gatt_characteristic_free( chrc->characteristic ); + winebluetooth_gatt_characteristic_free( handle ); + free( chrc ); + return; + } + } + LeaveCriticalSection( &svc->chars_cs ); + } + LeaveCriticalSection( &device->props_cs ); + } + LeaveCriticalSection( &radio->remote_devices_cs ); + } + LeaveCriticalSection( &device_list_cs ); + winebluetooth_gatt_characteristic_free( handle ); +} + static DWORD CALLBACK bluetooth_event_loop_thread_proc( void *arg ) { NTSTATUS status; @@ -1305,6 +1355,9 @@ static DWORD CALLBACK bluetooth_event_loop_thread_proc( void *arg ) case BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_ADDED: bluetooth_gatt_service_add_characteristic( event->event_data.gatt_characteristic_added ); break; + case BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_REMOVED: + bluetooth_gatt_characteristic_remove( event->event_data.gatt_characterisic_removed ); + break; default: FIXME( "Unknown bluetooth watcher event code: %#x\n", event->event_type ); } diff --git a/dlls/winebth.sys/winebth_priv.h b/dlls/winebth.sys/winebth_priv.h index e21b3b586d7..34582ed3759 100644 --- a/dlls/winebth.sys/winebth_priv.h +++ b/dlls/winebth.sys/winebth_priv.h @@ -254,6 +254,11 @@ static inline BOOL winebluetooth_gatt_service_equal( winebluetooth_gatt_service_ }
void winebluetooth_gatt_characteristic_free( winebluetooth_gatt_characteristic_t characteristic ); +static inline BOOL winebluetooth_gatt_characteristic_equal( winebluetooth_gatt_characteristic_t c1, + winebluetooth_gatt_characteristic_t c2) +{ + return c1.handle == c2.handle; +}
enum winebluetooth_watcher_event_type { @@ -267,6 +272,7 @@ enum winebluetooth_watcher_event_type BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_GATT_SERVICE_ADDED, BLUETOOTH_WATCHER_EVENT_TYPE_DEVICE_GATT_SERVICE_REMOVED, BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_ADDED, + BLUETOOTH_WATCHER_EVENT_TYPE_GATT_CHARACTERISTIC_REMOVED, };
struct winebluetooth_watcher_event_radio_added @@ -352,6 +358,7 @@ union winebluetooth_watcher_event_data struct winebluetooth_watcher_event_gatt_service_added gatt_service_added; winebluetooth_gatt_service_t gatt_service_removed; struct winebluetooth_watcher_event_gatt_characteristic_added gatt_characteristic_added; + winebluetooth_gatt_characteristic_t gatt_characterisic_removed; };
struct winebluetooth_watcher_event
From: Vibhav Pant vibhavp@gmail.com
--- dlls/bluetoothapis/bluetoothapis.spec | 2 +- dlls/bluetoothapis/gatt.c | 65 ++++++++++++++++++++++++++- include/bluetoothleapis.h | 1 + 3 files changed, 65 insertions(+), 3 deletions(-)
diff --git a/dlls/bluetoothapis/bluetoothapis.spec b/dlls/bluetoothapis/bluetoothapis.spec index 2cc00f46c9d..9d171a18013 100644 --- a/dlls/bluetoothapis/bluetoothapis.spec +++ b/dlls/bluetoothapis/bluetoothapis.spec @@ -35,7 +35,7 @@ @ stub BluetoothGATTBeginReliableWrite @ stub BluetoothGATTEndReliableWrite @ stub BluetoothGATTGetCharacteristicValue -@ stub BluetoothGATTGetCharacteristics +@ stdcall BluetoothGATTGetCharacteristics(ptr ptr long ptr ptr long) @ stub BluetoothGATTGetDescriptorValue @ stub BluetoothGATTGetDescriptors @ stub BluetoothGATTGetIncludedServices diff --git a/dlls/bluetoothapis/gatt.c b/dlls/bluetoothapis/gatt.c index 12b3ad00f78..3c648c757c5 100644 --- a/dlls/bluetoothapis/gatt.c +++ b/dlls/bluetoothapis/gatt.c @@ -38,6 +38,20 @@
WINE_DEFAULT_DEBUG_CHANNEL( bluetoothapis );
+static const char *debugstr_BTH_LE_UUID( const BTH_LE_UUID *uuid ) +{ + if (uuid->IsShortUuid) + return wine_dbg_sprintf("{ IsShortUuid=1 {%#x} }", uuid->Value.ShortUuid ); + return wine_dbg_sprintf( "{ IsShortUuid=0 %s }", debugstr_guid( &uuid->Value.LongUuid ) ); +} + +static const char *debugstr_BTH_LE_GATT_SERVICE( const BTH_LE_GATT_SERVICE *svc ) +{ + if (!svc) + return wine_dbg_sprintf( "(null)" ); + return wine_dbg_sprintf( "{ %s %#x }", debugstr_BTH_LE_UUID( &svc->ServiceUuid ), svc->AttributeHandle ); +} + HRESULT WINAPI BluetoothGATTGetServices( HANDLE le_device, USHORT count, BTH_LE_GATT_SERVICE *buf, USHORT *actual, ULONG flags ) { @@ -49,7 +63,7 @@ HRESULT WINAPI BluetoothGATTGetServices( HANDLE le_device, USHORT count, BTH_LE_ if (!actual) return E_POINTER;
- if ((!buf && count) || (buf && !count) || !actual) + if ((!buf && count) || (buf && !count)) return E_INVALIDARG;
for (;;) @@ -88,10 +102,57 @@ HRESULT WINAPI BluetoothGATTGetServices( HANDLE le_device, USHORT count, BTH_LE_ return HRESULT_FROM_WIN32( ERROR_MORE_DATA ); }
- memcpy( buf, services->services, min( services_count, count ) ); + memcpy( buf, services->services, min( services_count, count ) * sizeof( *buf ) ); free( services ); if (count < services_count) return HRESULT_FROM_WIN32( ERROR_INVALID_USER_BUFFER );
return S_OK; } + +HRESULT WINAPI BluetoothGATTGetCharacteristics( HANDLE device, BTH_LE_GATT_SERVICE *service, USHORT count, + BTH_LE_GATT_CHARACTERISTIC *buf, USHORT *actual, ULONG flags ) +{ + struct winebth_le_device_get_gatt_characteristics_params *chars; + DWORD size, bytes; + + TRACE( "(%p, %s, %u, %p, %p, %#lx)\n", device, debugstr_BTH_LE_GATT_SERVICE( service ), count, buf, actual, flags ); + + if (flags) + FIXME( "Unsupported flags: %#lx\n", flags ); + + if (!actual) + return E_POINTER; + + if ((buf && !count) || !service) + return E_INVALIDARG; + + size = offsetof( struct winebth_le_device_get_gatt_characteristics_params, characteristics[count] ); + chars = calloc( 1, size ); + if (!chars) + return HRESULT_FROM_WIN32( ERROR_NO_SYSTEM_RESOURCES ); + chars->service = *service; + if (!DeviceIoControl( device, IOCTL_WINEBTH_LE_DEVICE_GET_GATT_CHARACTERISTICS, chars, size, chars, + size, &bytes, NULL ) && GetLastError() != ERROR_MORE_DATA) + { + free( chars ); + return HRESULT_FROM_WIN32( GetLastError() ); + } + + *actual = chars->count; + if (!chars->count) + { + free( chars ); + return S_OK; + } + if (!buf) + { + free( chars ); + return HRESULT_FROM_WIN32( ERROR_MORE_DATA ); + } + memcpy( buf, chars->characteristics, min( count, chars->count ) * sizeof( *buf ) ); + if (count < chars->count) + return HRESULT_FROM_WIN32( ERROR_INVALID_USER_BUFFER ); + free( chars ); + return S_OK; +} diff --git a/include/bluetoothleapis.h b/include/bluetoothleapis.h index 64cdd366f7d..e790eb32aa4 100644 --- a/include/bluetoothleapis.h +++ b/include/bluetoothleapis.h @@ -25,6 +25,7 @@ extern "C" { #endif
HRESULT WINAPI BluetoothGATTGetServices( HANDLE, USHORT, BTH_LE_GATT_SERVICE *, USHORT *, ULONG ); +HRESULT WINAPI BluetoothGATTGetCharacteristics( HANDLE, BTH_LE_GATT_SERVICE *, USHORT, BTH_LE_GATT_CHARACTERISTIC *, USHORT *, ULONG );
#ifdef __cplusplus }
From: Vibhav Pant vibhavp@gmail.com
--- dlls/bluetoothapis/tests/gatt.c | 81 +++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 5 deletions(-)
diff --git a/dlls/bluetoothapis/tests/gatt.c b/dlls/bluetoothapis/tests/gatt.c index 0afe1438141..3b3f26b2418 100644 --- a/dlls/bluetoothapis/tests/gatt.c +++ b/dlls/bluetoothapis/tests/gatt.c @@ -88,8 +88,6 @@ static void test_for_all_le_devices( int line, void (*func)( HANDLE, void * ), v
static const char *debugstr_BTH_LE_UUID( const BTH_LE_UUID *uuid ) { - if (!uuid) - return wine_dbg_sprintf( "(null)" ); if (uuid->IsShortUuid) return wine_dbg_sprintf("{ IsShortUuid=1 {%#x} }", uuid->Value.ShortUuid ); return wine_dbg_sprintf( "{ IsShortUuid=0 %s }", debugstr_guid( &uuid->Value.LongUuid ) ); @@ -97,11 +95,44 @@ static const char *debugstr_BTH_LE_UUID( const BTH_LE_UUID *uuid )
static const char *debugstr_BTH_LE_GATT_SERVICE( const BTH_LE_GATT_SERVICE *svc ) { - if (!svc) - return wine_dbg_sprintf( "(null)" ); return wine_dbg_sprintf( "{ %s %#x }", debugstr_BTH_LE_UUID( &svc->ServiceUuid ), svc->AttributeHandle ); }
+static const char *debugstr_BTH_LE_GATT_CHARACTERISTIC( const BTH_LE_GATT_CHARACTERISTIC *chrc ) +{ + return wine_dbg_sprintf( "{ %#x %s %#x %#x %d %d %d %d %d %d %d %d}", chrc->ServiceHandle, + debugstr_BTH_LE_UUID( &chrc->CharacteristicUuid ), chrc->AttributeHandle, + chrc->CharacteristicValueHandle, chrc->IsBroadcastable, chrc->IsReadable, chrc->IsWritable, + chrc->IsWritableWithoutResponse, chrc->IsSignedWritable, chrc->IsNotifiable, + chrc->IsIndicatable, chrc->HasExtendedProperties ); +} + +static void test_service_BluetoothGATTGetCharacteristics( HANDLE device, BTH_LE_GATT_SERVICE *service ) +{ + HRESULT ret; + USHORT actual = 0, actual2 = 0, i; + BTH_LE_GATT_CHARACTERISTIC *buf = NULL, dummy; + + ret = BluetoothGATTGetCharacteristics( device, service, 0, &dummy, &actual, 0 ); + ok( ret == E_INVALIDARG, "got ret %#lx\n", ret ); + + ret = BluetoothGATTGetCharacteristics( device, service, 0, NULL, &actual, 0 ); + ok( ret == HRESULT_FROM_WIN32( ERROR_MORE_DATA ), "got ret %#lx\n", ret ); + + ret = BluetoothGATTGetCharacteristics( device, service, actual, NULL, &actual2, 0 ); + ok( ret == HRESULT_FROM_WIN32( ERROR_MORE_DATA ), "got ret %#lx\n", ret ); + ok( actual == actual2, "%u != %u\n", actual, actual2 ); + + buf = calloc( actual, sizeof( *buf ) ); + ret = BluetoothGATTGetCharacteristics( device, service, actual, buf, &actual, 0 ); + ok( ret == S_OK, "BluetoothGATTGetCharacteristics failed: %#lx\n", ret ); + + for (i = 0; i < actual; i++) + trace( "characteristic %u: %s\n", i, debugstr_BTH_LE_GATT_CHARACTERISTIC( &buf[i] ) ); + + free( buf ); +} + static void test_device_BluetoothGATTGetServices( HANDLE device, void *param ) { HRESULT ret; @@ -141,7 +172,12 @@ static void test_device_BluetoothGATTGetServices( HANDLE device, void *param ) }
for (i = 0; i < actual; i++) - trace( "service %u: %s\n", i, debugstr_BTH_LE_GATT_SERVICE( &buf[i] ) ); + { + winetest_push_context( "service %u", i ); + trace( "%s\n", debugstr_BTH_LE_GATT_SERVICE( &buf[i] ) ); + test_service_BluetoothGATTGetCharacteristics( device, &buf[i] ); + winetest_pop_context(); + }
free( buf ); } @@ -156,7 +192,42 @@ static void test_BluetoothGATTGetServices( void ) test_for_all_le_devices( __LINE__, test_device_BluetoothGATTGetServices, NULL ); }
+static void test_device_BluetoothGATTGetCharacteristics( HANDLE device, void *data ) +{ + HRESULT ret; + USHORT actual = 0; + BTH_LE_GATT_SERVICE svc = {.ServiceUuid = {TRUE, {.ShortUuid = 0xdead}}, 0xbeef}; + + ret = BluetoothGATTGetCharacteristics( device, NULL, 0, NULL, NULL, 0 ); + ok( ret == E_POINTER, "got ret %#lx\n", ret ); + + ret = BluetoothGATTGetCharacteristics( device, &svc, 0, NULL, NULL, 0 ); + ok( ret == E_POINTER, "got ret %#lx\n", ret ); + + ret = BluetoothGATTGetCharacteristics( device, &svc, 0, NULL, &actual, 0 ); + ok( ret == E_INVALIDARG, "got ret %#lx\n", ret ); +} + +static void test_BluetoothGATTGetCharacteristic( void ) +{ + HRESULT ret; + USHORT actual = 0; + BTH_LE_GATT_SERVICE svc; + + ret = BluetoothGATTGetCharacteristics( NULL, NULL, 0, NULL, NULL, 0 ); + ok( ret == E_POINTER, "got ret %#lx\n", ret ); + + ret = BluetoothGATTGetCharacteristics( NULL, &svc, 0, NULL, &actual, 0 ); + ok( ret == E_HANDLE, "got ret %#lx\n", ret ); + + ret = BluetoothGATTGetCharacteristics( INVALID_HANDLE_VALUE, &svc, 0, NULL, &actual, 0 ); + ok( ret == E_HANDLE, "got ret %#lx\n", ret ); + + test_for_all_le_devices( __LINE__, test_device_BluetoothGATTGetCharacteristics, NULL ); +} + START_TEST( gatt ) { test_BluetoothGATTGetServices(); + test_BluetoothGATTGetCharacteristic(); }
From: Vibhav Pant vibhavp@gmail.com
--- dlls/winebth.sys/winebth.c | 71 ++++++++++++++++++++++++++++++++++---- include/ddk/bthguid.h | 4 +++ 2 files changed, 68 insertions(+), 7 deletions(-)
diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index e8fe328e18a..f3a32342d00 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -29,8 +29,10 @@ #include <winbase.h> #include <winternl.h> #include <winnls.h> +#include <wtypes.h> #include <initguid.h> #include <devpkey.h> +#include <propkey.h> #include <bthsdpdef.h> #include <bluetoothapis.h> #include <bthdef.h> @@ -947,6 +949,57 @@ static void bluetooth_radio_remove_remote_device( struct winebluetooth_watcher_e winebluetooth_device_free( event.device ); }
+/* Caller should hold device->props_cs. */ +static void bluetooth_device_set_properties( struct bluetooth_remote_device *device, + const BYTE *adapter_addr, + const struct winebluetooth_device_properties *props, + winebluetooth_device_props_mask_t mask ) +{ + if (mask & WINEBLUETOOTH_DEVICE_PROPERTY_ADDRESS) + { + WCHAR addr_str[18], aep_id[59]; + const WCHAR *connection; + const BYTE *device_addr = device->props.address.rgBytes; + + connection = device->bthle_symlink_name.Buffer ? L"BluetoothLE#BluetoothLE" : L"Bluetooth#Bluetooth"; + swprintf( aep_id, ARRAY_SIZE( aep_id ), L"%s#%s%02x:%02x:%02x:%02x:%02x:%02x-%02x:%02x:%02x:%02x:%02x:%02x", + connection, connection, adapter_addr[0], adapter_addr[1], adapter_addr[2], adapter_addr[3], + adapter_addr[4], adapter_addr[5], device_addr[0], device_addr[1], device_addr[2], device_addr[3], + device_addr[4], device_addr[5] ); + IoSetDevicePropertyData( device->device_obj, (DEVPROPKEY *)&PKEY_Devices_Aep_AepId, LOCALE_NEUTRAL, 0, + DEVPROP_TYPE_STRING, sizeof( aep_id ), aep_id ); + + swprintf( addr_str, ARRAY_SIZE( addr_str ), L"%02x%02x%02x%02x%02x%02x", device_addr[0], device_addr[1], + device_addr[2], device_addr[3], device_addr[4], device_addr[5] ); + IoSetDevicePropertyData( device->device_obj, &DEVPKEY_Bluetooth_DeviceAddress, LOCALE_NEUTRAL, 0, + DEVPROP_TYPE_STRING, 26, addr_str ); + if (device->bthle_symlink_name.Buffer) + IoSetDeviceInterfacePropertyData( &device->bthle_symlink_name, + (DEVPROPKEY *)&PKEY_DeviceInterface_Bluetooth_DeviceAddress, + LOCALE_NEUTRAL, 0, DEVPROP_TYPE_STRING, 26, addr_str ); + + swprintf( addr_str, ARRAY_SIZE( addr_str ), L"%02x:%02x:%02x:%02x:%02x:%02x", device_addr[0], device_addr[1], + device_addr[2], device_addr[3], device_addr[4], device_addr[5] ); + IoSetDevicePropertyData( device->device_obj, (DEVPROPKEY *)&PKEY_Devices_Aep_DeviceAddress, LOCALE_NEUTRAL, 0, + DEVPROP_TYPE_STRING, sizeof( addr_str ), addr_str ); + } + if (mask & WINEBLUETOOTH_DEVICE_PROPERTY_CLASS) + IoSetDevicePropertyData( device->device_obj, &DEVPKEY_Bluetooth_ClassOfDevice, LOCALE_NEUTRAL, 0, + DEVPROP_TYPE_UINT32, sizeof( props->class ), (void *)&props->class ); + if (mask & WINEBLUETOOTH_DEVICE_PROPERTY_CONNECTED && props->connected) + { + FILETIME time = {0}; + + GetSystemTimeAsFileTime( &time ); + IoSetDevicePropertyData( device->device_obj, &DEVPKEY_Bluetooth_LastConnectedTime, LOCALE_NEUTRAL, 0, + DEVPROP_TYPE_FILETIME, sizeof( time ), (void *)&time ); + if (device->bthle_symlink_name.Buffer) + IoSetDeviceInterfacePropertyData( &device->bthle_symlink_name, + (DEVPROPKEY *)&PKEY_DeviceInterface_Bluetooth_LastConnectedTime, + LOCALE_NEUTRAL, 0, DEVPROP_TYPE_FILETIME, sizeof( time ), (void *)&time ); + } +} + static void bluetooth_radio_update_device_props( struct winebluetooth_watcher_event_device_props_changed event ) { BTH_DEVICE_INFO device_new_info = {0}; @@ -965,8 +1018,12 @@ static void bluetooth_radio_update_device_props( struct winebluetooth_watcher_ev if (winebluetooth_device_equal( event.device, device->device )) { BTH_DEVICE_INFO old_info = {0}; + BLUETOOTH_ADDRESS adapter_addr;
radio_obj = radio->device_obj; + EnterCriticalSection( &radio->props_cs ); + adapter_addr = radio->props.address; + LeaveCriticalSection( &radio->props_cs );
EnterCriticalSection( &device->props_cs ); winebluetooth_device_properties_to_info( device->props_mask, &device->props, &old_info ); @@ -988,6 +1045,7 @@ static void bluetooth_radio_update_device_props( struct winebluetooth_watcher_ev if (event.changed_props_mask & WINEBLUETOOTH_DEVICE_PROPERTY_CLASS) device->props.class = event.props.class; winebluetooth_device_properties_to_info( device->props_mask, &device->props, &device_new_info ); + bluetooth_device_set_properties( device, adapter_addr.rgBytes, &device->props, device->props_mask ); LeaveCriticalSection( &device->props_cs ); LeaveCriticalSection( &radio->remote_devices_cs );
@@ -1616,8 +1674,11 @@ static NTSTATUS WINAPI remote_device_pdo_pnp( DEVICE_OBJECT *device_obj, struct } case IRP_MN_START_DEVICE: { - WCHAR addr_str[13]; - BLUETOOTH_ADDRESS addr; + BLUETOOTH_ADDRESS adapter_addr; + + EnterCriticalSection( &ext->radio->props_cs ); + adapter_addr = ext->radio->props.address; + LeaveCriticalSection( &ext->radio->props_cs );
EnterCriticalSection( &ext->props_cs ); if (ext->le && @@ -1625,12 +1686,8 @@ static NTSTATUS WINAPI remote_device_pdo_pnp( DEVICE_OBJECT *device_obj, struct &ext->bthle_symlink_name )) IoSetDeviceInterfaceState( &ext->bthle_symlink_name, TRUE ); ext->started = TRUE; - addr = ext->props.address; + bluetooth_device_set_properties( ext, adapter_addr.rgBytes, &ext->props, ext->props_mask ); LeaveCriticalSection( &ext->props_cs ); - swprintf( addr_str, ARRAY_SIZE( addr_str ), L"%02x%02x%02x%02x%02x%02x", addr.rgBytes[0], addr.rgBytes[1], - addr.rgBytes[2], addr.rgBytes[3], addr.rgBytes[4], addr.rgBytes[5] ); - IoSetDevicePropertyData( device_obj, &DEVPKEY_Bluetooth_DeviceAddress, LOCALE_NEUTRAL, 0, DEVPROP_TYPE_STRING, - sizeof( addr_str ), addr_str ); ret = STATUS_SUCCESS; break; } diff --git a/include/ddk/bthguid.h b/include/ddk/bthguid.h index d042e13a37b..514747f92b5 100644 --- a/include/ddk/bthguid.h +++ b/include/ddk/bthguid.h @@ -21,5 +21,9 @@
/* DEVPROP_TYPE_STRING */ DEFINE_DEVPROPKEY( DEVPKEY_Bluetooth_DeviceAddress, 0x2bd67d8b,0x8beb,0x48d5,0x87,0xe0,0x6c,0xda,0x34,0x28,0x04,0x0a,1 ); +/* DEVPROP_TYPE_UINT32 */ +DEFINE_DEVPROPKEY( DEVPKEY_Bluetooth_ClassOfDevice, 0x2bd67d8b,0x8beb,0x48d5,0x87,0xe0,0x6c,0xda,0x34,0x28,0x04,0x0a,10 ); +/* DEVPROP_TYPE_FILETIME */ +DEFINE_DEVPROPKEY( DEVPKEY_Bluetooth_LastConnectedTime, 0x2bd67d8b,0x8beb,0x48d5,0x87,0xe0,0x6c,0xda,0x34,0x28,0x04,0x0a,11 );
#endif
This merge request was approved by Elizabeth Figura.