From: Vibhav Pant <vibhavp@gmail.com> --- dlls/bluetoothapis/tests/gatt.c | 159 ++++++++++++++++++++++++++++++-- include/bthledef.h | 32 +++++++ 2 files changed, 182 insertions(+), 9 deletions(-) diff --git a/dlls/bluetoothapis/tests/gatt.c b/dlls/bluetoothapis/tests/gatt.c index 89604f589dd..dec81a5a014 100644 --- a/dlls/bluetoothapis/tests/gatt.c +++ b/dlls/bluetoothapis/tests/gatt.c @@ -37,7 +37,18 @@ #include <wine/test.h> -static void test_for_all_le_devices( int line, void (*func)( HANDLE, void * ), void *data ) +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; +} + +static void test_for_all_le_devices( int line, void (*func)( HANDLE, const WCHAR *, void * ), void *data ) { char buffer[sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W) + MAX_PATH * sizeof( WCHAR )]; SP_DEVICE_INTERFACE_DETAIL_DATA_W *iface_detail = (SP_DEVICE_INTERFACE_DETAIL_DATA_W *)buffer; @@ -75,7 +86,7 @@ static void test_for_all_le_devices( int line, void (*func)( HANDLE, void * ), v FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); winetest_push_context( "device %lu", n++ ); trace( "Address: %s\n", debugstr_w( addr_str ) ); - func( device, data ); + func( device, addr_str, data ); winetest_pop_context(); CloseHandle( device ); found = TRUE; @@ -107,7 +118,69 @@ static const char *debugstr_BTH_LE_GATT_CHARACTERISTIC( const BTH_LE_GATT_CHARAC chrc->IsIndicatable, chrc->HasExtendedProperties ); } -static void test_service_BluetoothGATTGetCharacteristics( HANDLE device, BTH_LE_GATT_SERVICE *service ) +static void test_service_BluetoothGATTGetCharacteristics( HANDLE service, const BTH_LE_GATT_CHARACTERISTIC *chars, USHORT len ) +{ + HRESULT ret; + USHORT actual = 0, i, match = 0; + BTH_LE_GATT_CHARACTERISTIC *chars2; + + ret = BluetoothGATTGetCharacteristics( service, NULL, 0, NULL, &actual, 0 ); + todo_wine ok( ret == HRESULT_FROM_WIN32( ERROR_MORE_DATA ), "got ret %#lx\n", ret ); + if (!actual) + return; + + actual = len; + chars2 = calloc( actual, sizeof( *chars2 ) ); + ret = BluetoothGATTGetCharacteristics( service, NULL, actual, chars2, &actual, 0 ); + ok( ret == S_OK, "BluetoothGATTGetCharacteristics failed: %#lx\n", ret ); + ok( actual == len, "%u != %u\n", actual, len ); + + /* Characteristics retrieved using the device and GATT service handle should match. */ + for (i = 0; i < actual; i++) + { + const BTH_LE_GATT_CHARACTERISTIC *chrc1 = &chars[i]; + USHORT j; + + winetest_push_context( "chars[%u]", i ); + for (j = 0; j < actual; j++) + { + const BTH_LE_GATT_CHARACTERISTIC *chrc2 = &chars2[j]; + + if (IsBthLEUuidMatch( chrc1->CharacteristicUuid, chrc2->CharacteristicUuid ) + && chrc1->ServiceHandle == chrc2->ServiceHandle + && chrc1->AttributeHandle == chrc2->AttributeHandle) + { + winetest_push_context( "chars2[%u]", j ); + match++; + ok( chrc1->ServiceHandle == chrc2->ServiceHandle, "%#x != %#x\n", chrc1->ServiceHandle, + chrc2->ServiceHandle ); + ok( chrc1->AttributeHandle == chrc2->AttributeHandle, "%#x != %#x\n", chrc1->AttributeHandle, + chrc2->AttributeHandle ); + ok( chrc1->CharacteristicValueHandle == chrc2->CharacteristicValueHandle, "%#x != %#x\n", + chrc1->CharacteristicValueHandle, chrc2->CharacteristicValueHandle ); + +#define TEST_BOOL(field) ok( chrc1->field == chrc2->field, "%d != %d\n", chrc1->field, chrc2->field ) + TEST_BOOL(IsBroadcastable); + TEST_BOOL(IsReadable); + TEST_BOOL(IsWritable); + TEST_BOOL(IsWritableWithoutResponse); + TEST_BOOL(IsSignedWritable); + TEST_BOOL(IsNotifiable); + TEST_BOOL(IsIndicatable); + TEST_BOOL(HasExtendedProperties); +#undef TEST_BOOL + winetest_pop_context(); + break; + } + } + winetest_pop_context(); + } + + ok( actual == match, "%u != %u\n", actual, match ); + free( chars2 ); +} + +static void test_BluetoothGATTGetCharacteristics( HANDLE device, HANDLE service, BTH_LE_GATT_SERVICE *service_info ) { HRESULT ret; USHORT actual = 0, actual2 = 0, i; @@ -119,24 +192,83 @@ static void test_service_BluetoothGATTGetCharacteristics( HANDLE device, BTH_LE_ ret = BluetoothGATTGetCharacteristics( device, NULL, 0, NULL, &actual, 0 ); ok( ret == E_INVALIDARG, "got ret %#lx\n", ret ); - ret = BluetoothGATTGetCharacteristics( device, service, 0, NULL, &actual, 0 ); + ret = BluetoothGATTGetCharacteristics( device, service_info, 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 ); + ret = BluetoothGATTGetCharacteristics( device, service_info, 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 ); + ret = BluetoothGATTGetCharacteristics( device, service_info, 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] ) ); + if (service) + test_service_BluetoothGATTGetCharacteristics( service, buf, actual ); + else + skip( "Could not obtain handle to GATT service.\n" ); free( buf ); } -static void test_device_BluetoothGATTGetServices( HANDLE device, void *param ) +static HANDLE get_gatt_service_iface( const WCHAR *device_addr_str, const GUID *service_uuid ) +{ + char buffer[sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W) + MAX_PATH * sizeof( WCHAR )]; + SP_DEVICE_INTERFACE_DETAIL_DATA_W *iface_detail = (SP_DEVICE_INTERFACE_DETAIL_DATA_W *)buffer; + HANDLE device = INVALID_HANDLE_VALUE; + SP_DEVICE_INTERFACE_DATA iface_data; + DWORD idx = 0, ret; + HDEVINFO devinfo; + + devinfo = SetupDiGetClassDevsW( &GUID_BLUETOOTH_GATT_SERVICE_DEVICE_INTERFACE, NULL, NULL, + DIGCF_PRESENT | DIGCF_DEVICEINTERFACE ); + ret = GetLastError(); + ok( devinfo != INVALID_HANDLE_VALUE, "SetupDiGetClassDevsW failed: %lu\n", ret ); + if (devinfo == INVALID_HANDLE_VALUE) + return NULL; + + iface_detail->cbSize = sizeof( *iface_detail ); + iface_data.cbSize = sizeof( iface_data ); + while (SetupDiEnumDeviceInterfaces( devinfo, NULL, &GUID_BLUETOOTH_GATT_SERVICE_DEVICE_INTERFACE, idx++, + &iface_data )) + { + SP_DEVINFO_DATA devinfo_data = {0}; + WCHAR addr_str[13]; + DEVPROPTYPE type; + GUID uuid = {0}; + BOOL success; + + devinfo_data.cbSize = sizeof( devinfo_data ); + success = SetupDiGetDeviceInterfaceDetailW( devinfo, &iface_data, iface_detail, sizeof( buffer ), NULL, + &devinfo_data ); + ok( success, "SetupDiGetDeviceInterfaceDetailW failed: %lu\n", GetLastError() ); + addr_str[0] = 0; + success = SetupDiGetDevicePropertyW( devinfo, &devinfo_data, &DEVPKEY_Bluetooth_DeviceAddress, &type, (BYTE *)addr_str, + sizeof( addr_str ), NULL, 0 ); + ok( success, "SetupDiGetDevicePropertyW failed: %lu\n", GetLastError() ); + ok( type == DEVPROP_TYPE_STRING, "got type %#lx\n", type ); + if (wcsicmp( addr_str, device_addr_str )) + continue; + success = SetupDiGetDevicePropertyW( devinfo, &devinfo_data, &DEVPKEY_Bluetooth_ServiceGUID, &type, + (BYTE *)&uuid, sizeof( uuid ), NULL, 0 ); + ok( success, "SetupDiGetDevicePropertyW failed: %lu\n", GetLastError() ); + ok( type == DEVPROP_TYPE_GUID, "got type %#lx\n", type ); + if (!IsEqualGUID( &uuid, service_uuid )) + continue; + + device = CreateFileW( iface_detail->DevicePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ); + ret = GetLastError(); + break; + } + SetupDiDestroyDeviceInfoList( devinfo ); + ok( device != INVALID_HANDLE_VALUE || broken( ret == ERROR_GEN_FAILURE ), + "Could not find device interface for GATT service %s (error %lu) \n", debugstr_guid( service_uuid ), ret ); + return device == INVALID_HANDLE_VALUE ? NULL : device; +} + +static void test_device_BluetoothGATTGetServices( HANDLE device, const WCHAR *device_addr_str, void *param ) { HRESULT ret; USHORT actual = 0, i; @@ -176,9 +308,18 @@ static void test_device_BluetoothGATTGetServices( HANDLE device, void *param ) for (i = 0; i < actual; i++) { + HANDLE service; + GUID uuid; + winetest_push_context( "service %u", i ); trace( "%s\n", debugstr_BTH_LE_GATT_SERVICE( &buf[i] ) ); - test_service_BluetoothGATTGetCharacteristics( device, &buf[i] ); + + le_to_uuid( &buf[i].ServiceUuid, &uuid ); + service = get_gatt_service_iface( device_addr_str, &uuid ); + test_BluetoothGATTGetCharacteristics( device, service, &buf[i] ); + if (service) + CloseHandle( service ); + winetest_pop_context(); } @@ -195,7 +336,7 @@ static void test_BluetoothGATTGetServices( void ) test_for_all_le_devices( __LINE__, test_device_BluetoothGATTGetServices, NULL ); } -static void test_device_BluetoothGATTGetCharacteristics( HANDLE device, void *data ) +static void test_device_BluetoothGATTGetCharacteristics( HANDLE device, const WCHAR *addr, void *data ) { HRESULT ret; USHORT actual = 0; diff --git a/include/bthledef.h b/include/bthledef.h index b4d9323c4da..32585c06f8e 100644 --- a/include/bthledef.h +++ b/include/bthledef.h @@ -18,6 +18,10 @@ #ifndef __BTHLEDEFS_H #define __BTHLEDEFS_H +#ifdef __cplusplus +extern "C" { +#endif + typedef struct _BTH_LE_UUID { BOOLEAN IsShortUuid; @@ -53,4 +57,32 @@ typedef struct _BTH_LE_GATT_CHARACTERISTIC 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 ); + +FORCEINLINE BOOLEAN IsBthLEUuidMatch( BTH_LE_UUID u1, BTH_LE_UUID u2 ) +{ + GUID full1, full2; + + if (u1.IsShortUuid && u2.IsShortUuid) + return u1.Value.ShortUuid == u2.Value.ShortUuid; + + if (u1.IsShortUuid) + { + full1 = BTH_LE_ATT_BLUETOOTH_BASE_GUID; + full1.Data1 = u1.Value.ShortUuid; + } + else + full1 = u1.Value.LongUuid; + if (u2.IsShortUuid) + { + full2 = BTH_LE_ATT_BLUETOOTH_BASE_GUID; + full2.Data1 = u2.Value.ShortUuid; + } + else + full2 = u2.Value.LongUuid; + return !memcmp( &full1, &full2, sizeof( full1 ) ); +} + +#ifdef __cplusplus +} +#endif #endif -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10099