mirror of
https://gitlab.winehq.org/wine/wine.git
synced 2025-08-29 02:33:58 +02:00
1206 lines
38 KiB
C
1206 lines
38 KiB
C
/*
|
|
* Bluetooth APIs
|
|
*
|
|
* Copyright 2016 Austin English
|
|
* 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 <winternl.h>
|
|
#include <windef.h>
|
|
#include <winbase.h>
|
|
#include <winuser.h>
|
|
#include <winreg.h>
|
|
#include <winnls.h>
|
|
|
|
#include "bthsdpdef.h"
|
|
#include "cfgmgr32.h"
|
|
#include "setupapi.h"
|
|
#include "winioctl.h"
|
|
|
|
#include "initguid.h"
|
|
#include "bluetoothapis.h"
|
|
#include "bthledef.h"
|
|
#include "bthdef.h"
|
|
#include "bthioctl.h"
|
|
#include "wine/winebth.h"
|
|
|
|
#include "wine/debug.h"
|
|
#include "wine/list.h"
|
|
|
|
#include "resource.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(bluetoothapis);
|
|
|
|
static HINSTANCE instance;
|
|
|
|
struct bluetooth_find_radio_handle
|
|
{
|
|
HDEVINFO devinfo;
|
|
DWORD idx;
|
|
};
|
|
|
|
struct bluetooth_find_device
|
|
{
|
|
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 const char *debugstr_addr( const BYTE *addr )
|
|
{
|
|
if (!addr)
|
|
return wine_dbg_sprintf( "(null)" );
|
|
|
|
return wine_dbg_sprintf( "%02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5] );
|
|
}
|
|
|
|
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 *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 )
|
|
{
|
|
struct bluetooth_find_device *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 (!BluetoothFindNextDevice( hfind, info ))
|
|
{
|
|
free( hfind->device_list );
|
|
free( hfind );
|
|
SetLastError( ERROR_NO_MORE_ITEMS );
|
|
return NULL;
|
|
}
|
|
SetLastError( ERROR_SUCCESS );
|
|
return hfind;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* BluetoothFindFirstRadio
|
|
*/
|
|
HBLUETOOTH_RADIO_FIND WINAPI BluetoothFindFirstRadio( BLUETOOTH_FIND_RADIO_PARAMS *params, HANDLE *radio )
|
|
{
|
|
struct bluetooth_find_radio_handle *find;
|
|
HANDLE device_ret;
|
|
DWORD err;
|
|
|
|
TRACE( "(%p, %p)\n", params, radio );
|
|
|
|
if (!params)
|
|
{
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return NULL;
|
|
}
|
|
if (params->dwSize != sizeof( *params ))
|
|
{
|
|
SetLastError( ERROR_REVISION_MISMATCH );
|
|
return NULL;
|
|
}
|
|
if (!(find = calloc( 1, sizeof( *find ) )))
|
|
{
|
|
SetLastError( ERROR_OUTOFMEMORY );
|
|
return NULL;
|
|
}
|
|
|
|
find->devinfo = SetupDiGetClassDevsW( &GUID_BTHPORT_DEVICE_INTERFACE, NULL, NULL,
|
|
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE );
|
|
if (find->devinfo == INVALID_HANDLE_VALUE)
|
|
{
|
|
free( find );
|
|
return NULL;
|
|
}
|
|
|
|
if (BluetoothFindNextRadio( find, &device_ret ))
|
|
{
|
|
*radio = device_ret;
|
|
return find;
|
|
}
|
|
|
|
err = GetLastError();
|
|
BluetoothFindRadioClose( find );
|
|
SetLastError( err );
|
|
return NULL;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* BluetoothFindRadioClose
|
|
*/
|
|
BOOL WINAPI BluetoothFindRadioClose( HBLUETOOTH_RADIO_FIND find_handle )
|
|
{
|
|
struct bluetooth_find_radio_handle *find = find_handle;
|
|
|
|
TRACE( "(%p)\n", find_handle );
|
|
|
|
if (!find_handle)
|
|
{
|
|
SetLastError( ERROR_INVALID_HANDLE );
|
|
return FALSE;
|
|
}
|
|
|
|
SetupDiDestroyDeviceInfoList( find->devinfo );
|
|
free( find );
|
|
SetLastError( ERROR_SUCCESS );
|
|
return TRUE;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* BluetoothFindDeviceClose
|
|
*/
|
|
BOOL WINAPI BluetoothFindDeviceClose( HBLUETOOTH_DEVICE_FIND find_handle )
|
|
{
|
|
struct bluetooth_find_device *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;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* BluetoothFindNextRadio
|
|
*/
|
|
BOOL WINAPI BluetoothFindNextRadio( HBLUETOOTH_RADIO_FIND find_handle, HANDLE *radio )
|
|
{
|
|
char buffer[sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W) + MAX_PATH * sizeof( WCHAR )];
|
|
struct bluetooth_find_radio_handle *find = find_handle;
|
|
SP_DEVICE_INTERFACE_DETAIL_DATA_W *iface_detail = (SP_DEVICE_INTERFACE_DETAIL_DATA_W *)buffer;
|
|
SP_DEVICE_INTERFACE_DATA iface_data;
|
|
HANDLE device_ret;
|
|
BOOL found;
|
|
|
|
TRACE( "(%p, %p)\n", find_handle, radio );
|
|
|
|
if (!find_handle)
|
|
{
|
|
SetLastError( ERROR_INVALID_HANDLE );
|
|
return FALSE;
|
|
}
|
|
|
|
iface_detail->cbSize = sizeof( *iface_detail );
|
|
iface_data.cbSize = sizeof( iface_data );
|
|
found = FALSE;
|
|
while (SetupDiEnumDeviceInterfaces( find->devinfo, NULL, &GUID_BTHPORT_DEVICE_INTERFACE, find->idx++,
|
|
&iface_data ))
|
|
{
|
|
if (!SetupDiGetDeviceInterfaceDetailW( find->devinfo, &iface_data, iface_detail, sizeof( buffer ), NULL,
|
|
NULL ))
|
|
continue;
|
|
device_ret = CreateFileW( iface_detail->DevicePath, GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL );
|
|
if (device_ret != INVALID_HANDLE_VALUE)
|
|
{
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found)
|
|
*radio = device_ret;
|
|
|
|
return found;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* BluetoothGetRadioInfo
|
|
*/
|
|
DWORD WINAPI BluetoothGetRadioInfo( HANDLE radio, PBLUETOOTH_RADIO_INFO info )
|
|
{
|
|
BOOL ret;
|
|
DWORD bytes = 0;
|
|
BTH_LOCAL_RADIO_INFO radio_info = {0};
|
|
|
|
TRACE( "(%p, %p)\n", radio, info );
|
|
|
|
if (!radio || !info)
|
|
return ERROR_INVALID_PARAMETER;
|
|
if (info->dwSize != sizeof( *info ))
|
|
return ERROR_REVISION_MISMATCH;
|
|
|
|
ret = DeviceIoControl( radio, IOCTL_BTH_GET_LOCAL_INFO, NULL, 0, &radio_info, sizeof( radio_info ), &bytes, NULL );
|
|
if (!ret)
|
|
return GetLastError();
|
|
|
|
if (radio_info.localInfo.flags & BDIF_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)
|
|
MultiByteToWideChar( CP_ACP, 0, radio_info.localInfo.name, -1, info->szName, ARRAY_SIZE( info->szName ) );
|
|
|
|
info->lmpSubversion = radio_info.radioInfo.lmpSubversion;
|
|
info->manufacturer = radio_info.radioInfo.mfg;
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* BluetoothGetDeviceInfo
|
|
*/
|
|
DWORD WINAPI BluetoothGetDeviceInfo( HANDLE radio, BLUETOOTH_DEVICE_INFO *info )
|
|
{
|
|
const static BYTE addr_zero[6];
|
|
BTH_DEVICE_INFO_LIST *devices;
|
|
DWORD i, ret = ERROR_NOT_FOUND;
|
|
|
|
TRACE( "(%p, %p)\n", radio, info );
|
|
|
|
if (!radio)
|
|
return E_HANDLE;
|
|
if (!info)
|
|
return ERROR_INVALID_PARAMETER;
|
|
if (info->dwSize != sizeof( *info ))
|
|
return ERROR_REVISION_MISMATCH;
|
|
if (!memcmp( info->Address.rgBytes, addr_zero, sizeof( addr_zero ) ))
|
|
return ERROR_NOT_FOUND;
|
|
|
|
devices = radio_get_devices( radio );
|
|
if (!devices)
|
|
return GetLastError();
|
|
for (i = 0; i < devices->numOfDevices; i++)
|
|
{
|
|
if (devices->deviceList[i].address == info->Address.ullLong)
|
|
{
|
|
device_info_from_bth_info( info, &devices->deviceList[i] );
|
|
if (info->fConnected)
|
|
GetSystemTime( &info->stLastSeen );
|
|
else
|
|
FIXME( "semi-stub!\n" );
|
|
ret = ERROR_SUCCESS;
|
|
break;
|
|
}
|
|
}
|
|
|
|
free( devices );
|
|
return ret;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* BluetoothIsConnectable
|
|
*/
|
|
BOOL WINAPI BluetoothIsConnectable( HANDLE radio )
|
|
{
|
|
TRACE( "(%p)\n", radio );
|
|
|
|
if (!radio)
|
|
{
|
|
BLUETOOTH_FIND_RADIO_PARAMS params = {.dwSize = sizeof( params )};
|
|
HBLUETOOTH_RADIO_FIND find = BluetoothFindFirstRadio( ¶ms, &radio );
|
|
|
|
if (!find)
|
|
return FALSE;
|
|
for (;;)
|
|
{
|
|
if (BluetoothIsConnectable( radio ))
|
|
{
|
|
CloseHandle( radio );
|
|
BluetoothFindRadioClose( find );
|
|
return TRUE;
|
|
}
|
|
|
|
CloseHandle( radio );
|
|
if (!BluetoothFindNextRadio( find, &radio ))
|
|
{
|
|
BluetoothFindRadioClose( find );
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BTH_LOCAL_RADIO_INFO radio_info = {0};
|
|
DWORD bytes;
|
|
DWORD ret;
|
|
|
|
ret = DeviceIoControl( radio, IOCTL_BTH_GET_LOCAL_INFO, NULL, 0, &radio_info, sizeof( radio_info ), &bytes,
|
|
NULL );
|
|
if (!ret)
|
|
{
|
|
ERR( "DeviceIoControl failed: %#lx\n", GetLastError() );
|
|
return FALSE;
|
|
}
|
|
return !!(radio_info.flags & LOCAL_RADIO_CONNECTABLE);
|
|
}
|
|
}
|
|
|
|
/*********************************************************************
|
|
* BluetoothEnableIncomingConnections
|
|
*/
|
|
BOOL WINAPI BluetoothEnableIncomingConnections( HANDLE radio, BOOL enable )
|
|
{
|
|
TRACE( "(%p, %d)\n", radio, enable );
|
|
if (!radio)
|
|
{
|
|
BLUETOOTH_FIND_RADIO_PARAMS params = {.dwSize = sizeof( params )};
|
|
HBLUETOOTH_RADIO_FIND find = BluetoothFindFirstRadio( ¶ms, &radio );
|
|
|
|
if (!find)
|
|
return FALSE;
|
|
for (;;)
|
|
{
|
|
if (BluetoothEnableIncomingConnections( radio, enable ))
|
|
{
|
|
CloseHandle( radio );
|
|
BluetoothFindRadioClose( find );
|
|
return TRUE;
|
|
}
|
|
|
|
CloseHandle(radio );
|
|
if (!BluetoothFindNextRadio( find, &radio ))
|
|
{
|
|
BluetoothFindRadioClose( find );
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
else if (!enable && !BluetoothIsDiscoverable( radio ))
|
|
/* The local radio can only be made non-connectable if it is non-discoverable. */
|
|
return FALSE;
|
|
else
|
|
{
|
|
struct winebth_radio_set_flag_params params = {0};
|
|
BOOL ret;
|
|
DWORD bytes;
|
|
|
|
params.flag = LOCAL_RADIO_CONNECTABLE;
|
|
params.enable = !!enable;
|
|
ret = DeviceIoControl( radio, IOCTL_WINEBTH_RADIO_SET_FLAG, ¶ms, sizeof( params ), NULL, 0, &bytes, NULL );
|
|
if (!ret)
|
|
ERR("DeviceIoControl failed: %#lx\n", GetLastError());
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
/*********************************************************************
|
|
* BluetoothIsDiscoverable
|
|
*/
|
|
BOOL WINAPI BluetoothIsDiscoverable( HANDLE radio )
|
|
{
|
|
TRACE( "(%p)\n", radio );
|
|
|
|
if (!radio)
|
|
{
|
|
BLUETOOTH_FIND_RADIO_PARAMS params = {.dwSize = sizeof( params )};
|
|
HBLUETOOTH_RADIO_FIND find = BluetoothFindFirstRadio( ¶ms, &radio );
|
|
|
|
if (!find)
|
|
return FALSE;
|
|
for (;;)
|
|
{
|
|
if (BluetoothIsDiscoverable( radio ))
|
|
{
|
|
CloseHandle( radio );
|
|
BluetoothFindRadioClose( find );
|
|
return TRUE;
|
|
}
|
|
|
|
CloseHandle( radio );
|
|
if (!BluetoothFindNextRadio( find, &radio ))
|
|
{
|
|
BluetoothFindRadioClose( find );
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BTH_LOCAL_RADIO_INFO radio_info = {0};
|
|
DWORD bytes;
|
|
DWORD ret;
|
|
|
|
ret = DeviceIoControl( radio, IOCTL_BTH_GET_LOCAL_INFO, NULL, 0, &radio_info, sizeof( radio_info ), &bytes,
|
|
NULL );
|
|
if (!ret)
|
|
{
|
|
ERR( "DeviceIoControl failed: %#lx\n", GetLastError() );
|
|
return FALSE;
|
|
}
|
|
return !!(radio_info.flags & LOCAL_RADIO_DISCOVERABLE);
|
|
}
|
|
}
|
|
|
|
/*********************************************************************
|
|
* BluetoothEnableDiscovery
|
|
*/
|
|
BOOL WINAPI BluetoothEnableDiscovery( HANDLE radio, BOOL enabled )
|
|
{
|
|
TRACE( "(%p, %d)\n", radio, enabled );
|
|
if (!radio)
|
|
{
|
|
BLUETOOTH_FIND_RADIO_PARAMS params = {.dwSize = sizeof( params )};
|
|
HBLUETOOTH_RADIO_FIND find = BluetoothFindFirstRadio( ¶ms, &radio );
|
|
|
|
if (!find)
|
|
return FALSE;
|
|
for (;;)
|
|
{
|
|
if (BluetoothEnableDiscovery( radio, enabled ))
|
|
{
|
|
CloseHandle( radio );
|
|
BluetoothFindRadioClose( find );
|
|
return TRUE;
|
|
}
|
|
|
|
CloseHandle(radio );
|
|
if (!BluetoothFindNextRadio( find, &radio ))
|
|
{
|
|
BluetoothFindRadioClose( find );
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
else if (enabled && !BluetoothIsConnectable( radio ))
|
|
/* The local radio can only be made discoverable if it is connectable. */
|
|
return FALSE;
|
|
else
|
|
{
|
|
struct winebth_radio_set_flag_params params = {0};
|
|
BOOL ret;
|
|
DWORD bytes;
|
|
|
|
params.flag = LOCAL_RADIO_DISCOVERABLE;
|
|
params.enable = !!enabled;
|
|
ret = DeviceIoControl( radio, IOCTL_WINEBTH_RADIO_SET_FLAG, ¶ms, sizeof( params ), NULL, 0, &bytes, NULL );
|
|
if (!ret)
|
|
ERR("DeviceIoControl failed: %#lx\n", GetLastError());
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
/*********************************************************************
|
|
* BluetoothFindNextDevice
|
|
*/
|
|
BOOL WINAPI BluetoothFindNextDevice( HBLUETOOTH_DEVICE_FIND find, BLUETOOTH_DEVICE_INFO *info )
|
|
{
|
|
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;
|
|
}
|
|
|
|
struct bluetooth_auth_listener
|
|
{
|
|
struct list entry;
|
|
|
|
unsigned int all_devices : 1;
|
|
BLUETOOTH_ADDRESS addr;
|
|
|
|
PFN_AUTHENTICATION_CALLBACK_EX callback;
|
|
|
|
void *user_data;
|
|
};
|
|
|
|
struct bluetooth_auth_wizard_listener
|
|
{
|
|
struct list entry;
|
|
ULONG refcount;
|
|
|
|
BLUETOOTH_ADDRESS addr;
|
|
};
|
|
|
|
static const char *debugstr_bluetooth_auth_listener( const struct bluetooth_auth_listener *listener )
|
|
{
|
|
if (!listener)
|
|
return wine_dbg_sprintf( "(null)" );
|
|
if (listener->all_devices)
|
|
return wine_dbg_sprintf( "{ all_devices=1 %p %p }", listener->callback, listener->user_data );
|
|
return wine_dbg_sprintf( "{ all_devices=0 %s %p %p }", debugstr_addr( listener->addr.rgBytes ),
|
|
listener->callback, listener->user_data );
|
|
}
|
|
|
|
static SRWLOCK bluetooth_auth_lock = SRWLOCK_INIT;
|
|
/* A list of bluetooth_auth_listener objects added by BluetoothRegisterForAuthenticationEx.
|
|
* Guarded by bluetooth_auth_lock. */
|
|
static struct list bluetooth_auth_listeners = LIST_INIT( bluetooth_auth_listeners );
|
|
/* A list of fallback "listeners" created by BluetoothAuthenticateDeviceEx, used if no matching listener object
|
|
* could be found in bluetooth_auth_listeners. We use this to decide which pairing requests can be forwarded to a user
|
|
* wizard. Guarded by bluetooth_auth_lock. */
|
|
static struct list bluetooth_auth_wizard_listeners = LIST_INIT( bluetooth_auth_wizard_listeners );
|
|
static HCMNOTIFICATION bluetooth_auth_event_notify; /* Guarded by bluetooth_auth_lock */
|
|
|
|
struct auth_callback_data
|
|
{
|
|
PFN_AUTHENTICATION_CALLBACK_EX callback;
|
|
struct winebth_authentication_request request;
|
|
void *user_data;
|
|
};
|
|
|
|
static CALLBACK void tp_auth_callback_proc( TP_CALLBACK_INSTANCE *instance, void *ctx )
|
|
{
|
|
BLUETOOTH_AUTHENTICATION_CALLBACK_PARAMS params;
|
|
struct auth_callback_data *data = ctx;
|
|
|
|
device_info_from_bth_info( ¶ms.deviceInfo, &data->request.device_info );
|
|
/* For some reason, Windows uses the *local* time here. */
|
|
GetLocalTime( ¶ms.deviceInfo.stLastSeen );
|
|
GetLocalTime( ¶ms.deviceInfo.stLastUsed );
|
|
params.authenticationMethod = data->request.auth_method;
|
|
params.ioCapability = BLUETOOTH_IO_CAPABILITY_UNDEFINED;
|
|
params.authenticationRequirements = BLUETOOTH_MITM_ProtectionNotDefined;
|
|
params.Numeric_Value = data->request.numeric_value_or_passkey;
|
|
data->callback( data->user_data, ¶ms );
|
|
free( data );
|
|
}
|
|
|
|
static void bluetooth_auth_wizard_ask_response( struct bluetooth_auth_wizard_listener *listener,
|
|
const struct winebth_authentication_request *request )
|
|
{
|
|
WCHAR *msg = NULL, title[1024], format[1024], name_or_addr[BLUETOOTH_MAX_NAME_SIZE];
|
|
BLUETOOTH_FIND_RADIO_PARAMS find_params = {.dwSize = sizeof( find_params )};
|
|
struct winebth_radio_send_auth_response_params params = {0};
|
|
HBLUETOOTH_RADIO_FIND radio_find;
|
|
DWORD_PTR format_args[2];
|
|
BLUETOOTH_ADDRESS addr;
|
|
DWORD ret, bytes;
|
|
UINT resource_id;
|
|
HANDLE radio;
|
|
|
|
switch (request->auth_method)
|
|
{
|
|
case BLUETOOTH_AUTHENTICATION_METHOD_NUMERIC_COMPARISON:
|
|
case BLUETOOTH_AUTHENTICATION_METHOD_PASSKEY_NOTIFICATION:
|
|
break;
|
|
default:
|
|
FIXME( "Unsupported authentication method: %d\n", request->auth_method );
|
|
goto done;
|
|
}
|
|
|
|
addr.ullLong = request->device_info.address;
|
|
title[0] = format[0] = name_or_addr[0] = '\0';
|
|
if (request->device_info.flags & BDIF_NAME)
|
|
{
|
|
resource_id = IDS_AUTH_WIZARD_NAMED_PAIR_REQUEST;
|
|
MultiByteToWideChar( CP_ACP, 0, request->device_info.name, -1, name_or_addr, ARRAY_SIZE( name_or_addr ) );
|
|
}
|
|
else
|
|
{
|
|
resource_id = IDS_AUTH_WIZARD_UNNAMED_PAIR_REQUEST;
|
|
swprintf( name_or_addr, ARRAY_SIZE( name_or_addr ), 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] );
|
|
}
|
|
format_args[0] = (DWORD_PTR)name_or_addr;
|
|
format_args[1] = request->numeric_value_or_passkey;
|
|
if (LoadStringW( instance, resource_id, format, ARRAY_SIZE( format )))
|
|
FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
format, 0, 0, (WCHAR *)&msg, 0, (va_list *)format_args );
|
|
LoadStringW( instance, IDS_AUTH_WIZARD_PAIR_REQUEST_TITLE, title, ARRAY_SIZE( title ) );
|
|
ret = MessageBoxW( NULL, msg, title, MB_YESNO );
|
|
if (msg)
|
|
LocalFree( msg );
|
|
if (!ret)
|
|
{
|
|
ERR("MessageBoxW failed: %lu\n", ret );
|
|
goto done;
|
|
}
|
|
|
|
params.address = RtlUlonglongByteSwap( addr.ullLong ) >> 16;
|
|
params.method = request->auth_method;
|
|
params.numeric_value_or_passkey = request->numeric_value_or_passkey;
|
|
params.negative = ret != IDYES;
|
|
|
|
radio_find = BluetoothFindFirstRadio( &find_params, &radio );
|
|
if (!radio_find)
|
|
{
|
|
ERR( "BluetoothFindFirstRadio failed: %lu\n", GetLastError() );
|
|
goto done;
|
|
}
|
|
|
|
do {
|
|
if (DeviceIoControl( radio, IOCTL_WINEBTH_RADIO_SEND_AUTH_RESPONSE, ¶ms, sizeof( params ),
|
|
¶ms, sizeof( params ), &bytes, NULL ))
|
|
{
|
|
ret = ERROR_SUCCESS;
|
|
CloseHandle( radio );
|
|
break;
|
|
}
|
|
ret = GetLastError();
|
|
CloseHandle( radio );
|
|
} while (BluetoothFindNextRadio( radio_find, &radio ));
|
|
|
|
BluetoothFindRadioClose( radio_find );
|
|
if (ret)
|
|
ERR( "Failed to send pairing response: %lu\n", ret );
|
|
done:
|
|
if (!--listener->refcount)
|
|
{
|
|
list_remove( &listener->entry );
|
|
free( listener );
|
|
}
|
|
}
|
|
|
|
static CALLBACK DWORD bluetooth_auth_event_callback( HCMNOTIFICATION notify, void *ctx, CM_NOTIFY_ACTION action,
|
|
CM_NOTIFY_EVENT_DATA *event_data, DWORD size )
|
|
{
|
|
struct winebth_authentication_request *request = (struct winebth_authentication_request *)event_data->u.DeviceHandle.Data;
|
|
struct bluetooth_auth_listener *listener;
|
|
BOOL try_wizard = TRUE;
|
|
|
|
TRACE( "(%p, %p, %d, %p, %lu)\n", notify, ctx, action, event_data, size );
|
|
|
|
switch (action)
|
|
{
|
|
case CM_NOTIFY_ACTION_DEVICECUSTOMEVENT:
|
|
if (!IsEqualGUID( &event_data->u.DeviceHandle.EventGuid, &GUID_WINEBTH_AUTHENTICATION_REQUEST ))
|
|
{
|
|
FIXME( "Unexpected EventGUID: %s\n", debugstr_guid(&event_data->u.DeviceHandle.EventGuid) );
|
|
break;
|
|
}
|
|
|
|
AcquireSRWLockExclusive( &bluetooth_auth_lock );
|
|
LIST_FOR_EACH_ENTRY( listener, &bluetooth_auth_listeners, struct bluetooth_auth_listener, entry )
|
|
{
|
|
struct auth_callback_data *data;
|
|
|
|
if (!(listener->all_devices || listener->addr.ullLong == request->device_info.address))
|
|
continue;
|
|
try_wizard = FALSE;
|
|
data = calloc( 1, sizeof( *data ) );
|
|
if (!data)
|
|
continue;
|
|
|
|
data->request = *request;
|
|
data->callback = listener->callback;
|
|
data->user_data = listener->user_data;
|
|
if (!TrySubmitThreadpoolCallback( tp_auth_callback_proc, data, NULL ))
|
|
{
|
|
ERR( "TrySubmitThreadpoolCallback failed: %lu\n", GetLastError() );
|
|
free( data );
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/* No appropriate registered callback could be found for this request. Lets ask the user what to do instead. */
|
|
if (try_wizard)
|
|
{
|
|
struct bluetooth_auth_wizard_listener *listener, *next;
|
|
LIST_FOR_EACH_ENTRY_SAFE( listener, next, &bluetooth_auth_wizard_listeners,
|
|
struct bluetooth_auth_wizard_listener, entry )
|
|
{
|
|
if (listener->addr.ullLong == request->device_info.address)
|
|
{
|
|
bluetooth_auth_wizard_ask_response( listener, request );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
ReleaseSRWLockExclusive( &bluetooth_auth_lock );
|
|
break;
|
|
default:
|
|
FIXME( "Unexpected CM_NOTIFY_ACTION: %d\n", action );
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static DWORD bluetooth_auth_register( void )
|
|
{
|
|
DWORD ret;
|
|
CM_NOTIFY_FILTER handle_filter = {0};
|
|
HANDLE winebth_auth;
|
|
DWORD bytes;
|
|
|
|
|
|
winebth_auth = CreateFileW( WINEBTH_AUTH_DEVICE_PATH, GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL );
|
|
if (winebth_auth == INVALID_HANDLE_VALUE)
|
|
return GetLastError();
|
|
|
|
handle_filter.cbSize = sizeof( handle_filter );
|
|
handle_filter.FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE;
|
|
handle_filter.u.DeviceHandle.hTarget = winebth_auth;
|
|
|
|
ret = CM_Register_Notification( &handle_filter, NULL, bluetooth_auth_event_callback,
|
|
&bluetooth_auth_event_notify );
|
|
if (ret)
|
|
{
|
|
ERR( "CM_Register_Notification failed: %#lx\n", ret );
|
|
CloseHandle( winebth_auth );
|
|
return CM_MapCrToWin32Err( ret, ERROR_INTERNAL_ERROR );
|
|
}
|
|
|
|
if (!DeviceIoControl( winebth_auth, IOCTL_WINEBTH_AUTH_REGISTER, NULL, 0, NULL, 0, &bytes, NULL ))
|
|
ret = GetLastError();
|
|
|
|
CloseHandle( winebth_auth );
|
|
return ret;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* BluetoothRegisterForAuthenticationEx
|
|
*/
|
|
DWORD WINAPI BluetoothRegisterForAuthenticationEx( const BLUETOOTH_DEVICE_INFO *device_info,
|
|
HBLUETOOTH_AUTHENTICATION_REGISTRATION *out,
|
|
PFN_AUTHENTICATION_CALLBACK_EX callback, void *param )
|
|
{
|
|
DWORD ret;
|
|
struct bluetooth_auth_listener *listener;
|
|
|
|
TRACE( "(%p, %p, %p, %p)\n", device_info, out, callback, param );
|
|
|
|
if (!out || (device_info && device_info->dwSize != sizeof ( *device_info )) || !callback)
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
listener = calloc( 1, sizeof( *listener ) );
|
|
if (!listener)
|
|
return ERROR_OUTOFMEMORY;
|
|
listener->all_devices = !device_info;
|
|
if (!listener->all_devices)
|
|
{
|
|
TRACE( "Registering for authentication events from %s\n", debugstr_addr( device_info->Address.rgBytes ) );
|
|
listener->addr = device_info->Address;
|
|
}
|
|
listener->callback = callback;
|
|
listener->user_data = param;
|
|
|
|
AcquireSRWLockExclusive( &bluetooth_auth_lock );
|
|
if (list_empty( &bluetooth_auth_listeners ))
|
|
{
|
|
ret = bluetooth_auth_register();
|
|
if (ret)
|
|
{
|
|
free( listener );
|
|
ReleaseSRWLockExclusive( &bluetooth_auth_lock );
|
|
return ret;
|
|
}
|
|
}
|
|
/* The MSDN documentation for BluetoothRegisterForAuthentication states if applications call this method
|
|
* multiple times, only the first callback registered is invoked during an authentication session.
|
|
* This is incorrect, at least for Windows 11, where all registered callbacks do get called. */
|
|
list_add_tail( &bluetooth_auth_listeners, &listener->entry );
|
|
ReleaseSRWLockExclusive( &bluetooth_auth_lock );
|
|
|
|
*out = listener;
|
|
ret = ERROR_SUCCESS;
|
|
return ret;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* BluetoothUnregisterAuthentication
|
|
*/
|
|
BOOL WINAPI BluetoothUnregisterAuthentication( HBLUETOOTH_AUTHENTICATION_REGISTRATION handle )
|
|
{
|
|
struct bluetooth_auth_listener *listener = handle;
|
|
DWORD ret = ERROR_SUCCESS;
|
|
|
|
TRACE( "(%s)\n", debugstr_bluetooth_auth_listener( handle ) );
|
|
|
|
if (!handle)
|
|
{
|
|
SetLastError( ERROR_INVALID_HANDLE );
|
|
return FALSE;
|
|
}
|
|
|
|
AcquireSRWLockExclusive( &bluetooth_auth_lock );
|
|
list_remove( &listener->entry );
|
|
if (list_empty( &bluetooth_auth_listeners ))
|
|
{
|
|
ret = CM_Unregister_Notification( bluetooth_auth_event_notify );
|
|
if (ret)
|
|
ERR( "CM_Unregister_Notification failed: %#lx\n", ret );
|
|
}
|
|
ReleaseSRWLockExclusive( &bluetooth_auth_lock );
|
|
|
|
SetLastError( ret );
|
|
return !ret;
|
|
}
|
|
|
|
static const char *debugstr_BLUETOOTH_AUTHENTICATE_RESPONSE( const BLUETOOTH_AUTHENTICATE_RESPONSE *resp )
|
|
{
|
|
if (!resp)
|
|
return wine_dbg_sprintf("(null)");
|
|
return wine_dbg_sprintf("{ bthAddressRemote: %s, authMethod: %d, negativeResponse: %d }",
|
|
debugstr_addr( resp->bthAddressRemote.rgBytes ), resp->authMethod, resp->negativeResponse );
|
|
}
|
|
|
|
DWORD WINAPI BluetoothSendAuthenticationResponseEx( HANDLE handle_radio, BLUETOOTH_AUTHENTICATE_RESPONSE *auth_response )
|
|
{
|
|
struct winebth_radio_send_auth_response_params params = {0};
|
|
DWORD ret, bytes;
|
|
|
|
TRACE( "(%p, %s)\n", handle_radio, debugstr_BLUETOOTH_AUTHENTICATE_RESPONSE( auth_response ) );
|
|
|
|
if (!auth_response)
|
|
return ERROR_INVALID_PARAMETER;
|
|
switch (auth_response->authMethod)
|
|
{
|
|
case BLUETOOTH_AUTHENTICATION_METHOD_NUMERIC_COMPARISON:
|
|
break;
|
|
case BLUETOOTH_AUTHENTICATION_METHOD_LEGACY:
|
|
case BLUETOOTH_AUTHENTICATION_METHOD_OOB:
|
|
case BLUETOOTH_AUTHENTICATION_METHOD_PASSKEY_NOTIFICATION:
|
|
case BLUETOOTH_AUTHENTICATION_METHOD_PASSKEY:
|
|
FIXME( "Unsupported authMethod: %d\n", auth_response->authMethod );
|
|
return ERROR_CALL_NOT_IMPLEMENTED;
|
|
default:
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
if (!handle_radio)
|
|
{
|
|
HBLUETOOTH_RADIO_FIND radio_find;
|
|
BLUETOOTH_FIND_RADIO_PARAMS find_params = {.dwSize = sizeof( find_params ) };
|
|
|
|
radio_find = BluetoothFindFirstRadio( &find_params, &handle_radio );
|
|
if (!radio_find)
|
|
return GetLastError();
|
|
for(;;)
|
|
{
|
|
ret = BluetoothSendAuthenticationResponseEx( handle_radio, auth_response );
|
|
CloseHandle( handle_radio );
|
|
if (!ret)
|
|
{
|
|
BluetoothFindRadioClose( radio_find );
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
if (!BluetoothFindNextRadio( radio_find, &handle_radio ))
|
|
{
|
|
BluetoothFindRadioClose( radio_find );
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
|
|
AcquireSRWLockShared( &bluetooth_auth_lock );
|
|
if (list_empty( &bluetooth_auth_listeners ))
|
|
{
|
|
ret = ERROR_INVALID_PARAMETER;
|
|
goto done;
|
|
}
|
|
params.address = RtlUlonglongByteSwap( auth_response->bthAddressRemote.ullLong ) >> 16;
|
|
params.method = auth_response->authMethod;
|
|
params.numeric_value_or_passkey = auth_response->numericCompInfo.NumericValue;
|
|
params.negative = !!auth_response->negativeResponse;
|
|
|
|
if (!DeviceIoControl( handle_radio, IOCTL_WINEBTH_RADIO_SEND_AUTH_RESPONSE, ¶ms, sizeof( params ),
|
|
¶ms, sizeof( params ), &bytes, NULL ))
|
|
{
|
|
ret = GetLastError();
|
|
goto done;
|
|
}
|
|
ret = params.authenticated ? ERROR_SUCCESS : ERROR_NOT_AUTHENTICATED;
|
|
|
|
done:
|
|
ReleaseSRWLockShared( &bluetooth_auth_lock );
|
|
return ret;
|
|
}
|
|
|
|
DWORD WINAPI BluetoothAuthenticateDeviceEx( HWND parent, HANDLE handle_radio, BLUETOOTH_DEVICE_INFO *device_info,
|
|
BLUETOOTH_OOB_DATA_INFO *oob_info,
|
|
AUTHENTICATION_REQUIREMENTS auth_req )
|
|
{
|
|
OVERLAPPED ovl = {0};
|
|
struct bluetooth_auth_wizard_listener *listener;
|
|
BOOL wizard_listener_exists;
|
|
BTH_ADDR device_addr;
|
|
BOOL success;
|
|
DWORD ret, bytes;
|
|
|
|
FIXME( "(%p, %p, %p, %p, %d): semi-stub!\n", parent, handle_radio, device_info, oob_info, auth_req );
|
|
|
|
if (!device_info || auth_req < MITMProtectionNotRequired || auth_req > MITMProtectionNotDefined)
|
|
return ERROR_INVALID_PARAMETER;
|
|
if (device_info->dwSize != sizeof( *device_info ))
|
|
return ERROR_REVISION_MISMATCH;
|
|
|
|
TRACE( "Initiating pairing with %s\n", debugstr_addr( device_info->Address.rgBytes ) );
|
|
if (!handle_radio)
|
|
{
|
|
BLUETOOTH_FIND_RADIO_PARAMS find_params = {.dwSize = sizeof( find_params )};
|
|
HBLUETOOTH_RADIO_FIND radio_find;
|
|
|
|
radio_find = BluetoothFindFirstRadio( &find_params, &handle_radio );
|
|
ret = GetLastError();
|
|
if (!radio_find)
|
|
return ret == ERROR_NO_MORE_ITEMS ? ERROR_NOT_FOUND : ret;
|
|
do {
|
|
ret = BluetoothAuthenticateDeviceEx( parent, handle_radio, device_info, oob_info, auth_req );
|
|
CloseHandle( handle_radio );
|
|
if (!ret || ret == ERROR_NO_MORE_ITEMS)
|
|
break;
|
|
} while (BluetoothFindNextRadio( radio_find, &handle_radio ));
|
|
|
|
BluetoothFindRadioClose( radio_find );
|
|
return ret;
|
|
}
|
|
|
|
AcquireSRWLockExclusive( &bluetooth_auth_lock );
|
|
if (list_empty( &bluetooth_auth_listeners ) && list_empty( &bluetooth_auth_wizard_listeners ))
|
|
{
|
|
ret = bluetooth_auth_register();
|
|
if (ret)
|
|
{
|
|
ReleaseSRWLockExclusive( &bluetooth_auth_lock );
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
wizard_listener_exists = FALSE;
|
|
LIST_FOR_EACH_ENTRY( listener, &bluetooth_auth_wizard_listeners, struct bluetooth_auth_wizard_listener, entry )
|
|
{
|
|
if (listener->addr.ullLong == device_info->Address.ullLong)
|
|
{
|
|
listener->refcount++;
|
|
wizard_listener_exists = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (!wizard_listener_exists)
|
|
{
|
|
listener = calloc( 1, sizeof( *listener ) );
|
|
if (!listener)
|
|
{
|
|
ReleaseSRWLockExclusive( &bluetooth_auth_lock );
|
|
return ERROR_OUTOFMEMORY;
|
|
}
|
|
listener->addr = device_info->Address;
|
|
list_add_tail( &bluetooth_auth_wizard_listeners, &listener->entry );
|
|
}
|
|
ReleaseSRWLockExclusive( &bluetooth_auth_lock );
|
|
|
|
ovl.hEvent = CreateEventW( NULL, TRUE, FALSE, NULL );
|
|
device_addr = RtlUlonglongByteSwap( device_info->Address.ullLong ) >> 16;
|
|
success = DeviceIoControl( handle_radio, IOCTL_WINEBTH_RADIO_START_AUTH, &device_addr, sizeof( device_addr ),
|
|
NULL, 0, &bytes, &ovl );
|
|
ret = ERROR_SUCCESS;
|
|
if (!success)
|
|
{
|
|
ret = GetLastError();
|
|
if (ret == ERROR_IO_PENDING)
|
|
{
|
|
ret = ERROR_SUCCESS;
|
|
if (!GetOverlappedResult( handle_radio, &ovl, &bytes, TRUE ))
|
|
ret = GetLastError();
|
|
}
|
|
}
|
|
CloseHandle( ovl.hEvent );
|
|
|
|
return ret;
|
|
}
|
|
|
|
DWORD WINAPI BluetoothRemoveDevice( BLUETOOTH_ADDRESS *addr )
|
|
{
|
|
BLUETOOTH_FIND_RADIO_PARAMS find_params = {0};
|
|
HBLUETOOTH_RADIO_FIND radio_find;
|
|
const static BYTE addr_zero[6];
|
|
BOOL success = FALSE;
|
|
HANDLE radio;
|
|
DWORD ret;
|
|
|
|
TRACE( "(%s)\n", debugstr_addr( (BYTE *)addr ) );
|
|
|
|
if (!memcmp( addr_zero, &addr->rgBytes, sizeof( addr_zero ) ))
|
|
return ERROR_NOT_FOUND;
|
|
|
|
find_params.dwSize = sizeof( find_params );
|
|
radio_find = BluetoothFindFirstRadio( &find_params, &radio );
|
|
if (!radio_find)
|
|
{
|
|
ret = GetLastError();
|
|
return ret == ERROR_NO_MORE_ITEMS ? ERROR_NOT_FOUND : ret;
|
|
}
|
|
|
|
do {
|
|
DWORD bytes;
|
|
success |= DeviceIoControl( radio, IOCTL_WINEBTH_RADIO_REMOVE_DEVICE, addr, sizeof( *addr ), NULL, 0, &bytes, NULL );
|
|
CloseHandle( radio );
|
|
} while (BluetoothFindNextRadio( radio_find, &radio ));
|
|
|
|
BluetoothFindRadioClose( radio_find );
|
|
return success ? ERROR_SUCCESS : ERROR_NOT_FOUND;
|
|
}
|
|
|
|
BOOL WINAPI DllMain( HINSTANCE inst, DWORD reason, void *reserved )
|
|
{
|
|
TRACE( "(%p, %lu, %p)\n", inst, reason, reserved );
|
|
|
|
if (reason == DLL_PROCESS_ATTACH)
|
|
instance = inst;
|
|
|
|
return TRUE;
|
|
}
|