Merge branch 'vibhavp/winebth-sys' into 'master'

winebth.sys part 1: Introduce Bluetooth driver.

See merge request wine/wine!6621
This commit is contained in:
Vibhav Pant 2024-11-19 22:26:48 +00:00
commit 3248582970
18 changed files with 1567 additions and 0 deletions

View file

@ -49,6 +49,11 @@ F: dlls/x3daudio*/
F: dlls/xapofx*/
F: dlls/xaudio*/
Bluetooth support
M: Vibhav Pant <vibhavp@gmail.com>
F: dlls/winebth.sys/
F: dlls/bluetoothapis/sdp.c
Common Controls Library
P: Nikolay Sivov <nsivov@codeweavers.com>
P: Zhiyi Zhang <zzhang@codeweavers.com>

2
configure generated vendored
View file

@ -1511,6 +1511,7 @@ enable_windowscodecs
enable_windowscodecsext
enable_winealsa_drv
enable_wineandroid_drv
enable_winebth_sys
enable_winebus_sys
enable_winecoreaudio_drv
enable_winecrt0
@ -22854,6 +22855,7 @@ wine_fn_config_makefile dlls/windowscodecsext enable_windowscodecsext
wine_fn_config_makefile dlls/windowscodecsext/tests enable_tests
wine_fn_config_makefile dlls/winealsa.drv enable_winealsa_drv
wine_fn_config_makefile dlls/wineandroid.drv enable_wineandroid_drv
wine_fn_config_makefile dlls/winebth.sys enable_winebth_sys
wine_fn_config_makefile dlls/winebus.sys enable_winebus_sys
wine_fn_config_makefile dlls/winecoreaudio.drv enable_winecoreaudio_drv
wine_fn_config_makefile dlls/winecrt0 enable_winecrt0

View file

@ -3252,6 +3252,7 @@ WINE_CONFIG_MAKEFILE(dlls/windowscodecsext)
WINE_CONFIG_MAKEFILE(dlls/windowscodecsext/tests)
WINE_CONFIG_MAKEFILE(dlls/winealsa.drv)
WINE_CONFIG_MAKEFILE(dlls/wineandroid.drv)
WINE_CONFIG_MAKEFILE(dlls/winebth.sys)
WINE_CONFIG_MAKEFILE(dlls/winebus.sys)
WINE_CONFIG_MAKEFILE(dlls/winecoreaudio.drv)
WINE_CONFIG_MAKEFILE(dlls/winecrt0)

View file

@ -0,0 +1,13 @@
MODULE = winebth.sys
IMPORTS = ntoskrnl uuid
UNIXLIB = winebth.so
UNIX_CFLAGS = $(DBUS_CFLAGS)
EXTRADLLFLAGS = -Wl,--subsystem,native
SOURCES = \
dbus.c \
unixlib.c \
winebluetooth.c \
winebth.c \
winebth.rc

367
dlls/winebth.sys/dbus.c Normal file
View file

@ -0,0 +1,367 @@
/*
* Support for communicating with BlueZ over DBus.
*
* Copyright 2024 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
*/
#if 0
#pragma makedep unix
#endif
#include "config.h"
#include <stdlib.h>
#include <dlfcn.h>
#include <assert.h>
#include <pthread.h>
#ifdef SONAME_LIBDBUS_1
#include <dbus/dbus.h>
#endif
#include <ntstatus.h>
#define WIN32_NO_STATUS
#include <windef.h>
#include <winternl.h>
#include <winbase.h>
#include <bthsdpdef.h>
#include <bluetoothapis.h>
#include <wine/debug.h>
#include "winebth_priv.h"
#include "unixlib.h"
#include "unixlib_priv.h"
#include "dbus.h"
WINE_DEFAULT_DEBUG_CHANNEL( winebth );
WINE_DECLARE_DEBUG_CHANNEL( dbus );
#ifdef SONAME_LIBDBUS_1
const int bluez_timeout = -1;
#define DBUS_INTERFACE_OBJECTMANAGER "org.freedesktop.DBus.ObjectManager"
#define BLUEZ_DEST "org.bluez"
#define BLUEZ_INTERFACE_ADAPTER "org.bluez.Adapter1"
#define DO_FUNC( f ) typeof( f ) (*p_##f)
DBUS_FUNCS;
#undef DO_FUNC
static BOOL load_dbus_functions( void )
{
void *handle = dlopen( SONAME_LIBDBUS_1, RTLD_NOW );
if (handle == NULL) goto failed;
#define DO_FUNC( f ) \
if (!( p_##f = dlsym( handle, #f ) )) \
{ \
ERR( "failed to load symbol %s: %s\n", #f, dlerror() ); \
goto failed; \
}
DBUS_FUNCS;
#undef DO_FUNC
return TRUE;
failed:
WARN( "failed to load DBus support: %s\n", dlerror() );
return FALSE;
}
static const char *bluez_next_dict_entry( DBusMessageIter *iter, DBusMessageIter *variant )
{
DBusMessageIter sub;
const char *name;
if (p_dbus_message_iter_get_arg_type( iter ) != DBUS_TYPE_DICT_ENTRY)
return NULL;
p_dbus_message_iter_recurse( iter, &sub );
p_dbus_message_iter_next( iter );
p_dbus_message_iter_get_basic( &sub, &name );
p_dbus_message_iter_next( &sub );
p_dbus_message_iter_recurse( &sub, variant );
return name;
}
static inline const char *dbgstr_dbus_connection( DBusConnection *connection )
{
return wine_dbg_sprintf( "{%p connected=%d}", connection,
p_dbus_connection_get_is_connected( connection ) );
}
static NTSTATUS bluez_get_objects_async( DBusConnection *connection, DBusPendingCall **call )
{
DBusMessage *request;
dbus_bool_t success;
TRACE( "Getting managed objects under '/' at service '%s'\n", BLUEZ_DEST );
request = p_dbus_message_new_method_call(
BLUEZ_DEST, "/", DBUS_INTERFACE_OBJECTMANAGER, "GetManagedObjects" );
if (!request)
{
return STATUS_NO_MEMORY;
}
success = p_dbus_connection_send_with_reply( connection, request, call, -1 );
p_dbus_message_unref( request );
if (!success)
return STATUS_NO_MEMORY;
if (*call == NULL)
return STATUS_INVALID_PARAMETER;
return STATUS_SUCCESS;
}
#define DBUS_OBJECTMANAGER_METHOD_GETMANAGEDOBJECTS_RETURN_SIGNATURE \
DBUS_TYPE_ARRAY_AS_STRING \
DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING \
DBUS_TYPE_OBJECT_PATH_AS_STRING \
DBUS_TYPE_ARRAY_AS_STRING \
DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING \
DBUS_TYPE_STRING_AS_STRING \
DBUS_TYPE_ARRAY_AS_STRING \
DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING \
DBUS_TYPE_STRING_AS_STRING \
DBUS_TYPE_VARIANT_AS_STRING \
DBUS_DICT_ENTRY_END_CHAR_AS_STRING \
DBUS_DICT_ENTRY_END_CHAR_AS_STRING \
DBUS_DICT_ENTRY_END_CHAR_AS_STRING
struct bluez_watcher_ctx
{
void *init_device_list_call;
/* struct bluez_init_entry */
struct list initial_radio_list;
};
void *bluez_dbus_init( void )
{
DBusError error;
DBusConnection *connection;
if (!load_dbus_functions()) return NULL;
p_dbus_threads_init_default();
p_dbus_error_init ( &error );
connection = p_dbus_bus_get_private ( DBUS_BUS_SYSTEM, &error );
if (!connection)
{
ERR( "Failed to get system dbus connection: %s: %s\n", debugstr_a( error.name ), debugstr_a( error.message ) );
p_dbus_error_free( &error );
return NULL;
}
return connection;
}
void bluez_dbus_close( void *connection )
{
TRACE_(dbus)( "(%s)\n", dbgstr_dbus_connection( connection ) );
p_dbus_connection_flush( connection );
p_dbus_connection_close( connection );
}
void bluez_dbus_free( void *connection )
{
TRACE_(dbus)( "(%s)\n", dbgstr_dbus_connection( connection ) );
p_dbus_connection_unref( connection );
}
struct bluez_watcher_event
{
struct list entry;
enum winebluetooth_watcher_event_type event_type;
union winebluetooth_watcher_event_data event;
};
NTSTATUS bluez_watcher_init( void *connection, void **ctx )
{
NTSTATUS status;
DBusPendingCall *call;
struct bluez_watcher_ctx *watcher_ctx =
calloc( 1, sizeof( struct bluez_watcher_ctx ) );
if (watcher_ctx == NULL) return STATUS_NO_MEMORY;
status = bluez_get_objects_async( connection, &call );
if (status != STATUS_SUCCESS)
{
free( watcher_ctx );
ERR( "could not create async GetManagedObjects call: %#x\n", (int)status);
return status;
}
watcher_ctx->init_device_list_call = call;
list_init( &watcher_ctx->initial_radio_list );
*ctx = watcher_ctx;
TRACE( "ctx=%p\n", ctx );
return STATUS_SUCCESS;
}
struct bluez_init_entry
{
union {
struct winebluetooth_watcher_event_radio_added radio;
} object;
struct list entry;
};
static NTSTATUS bluez_build_initial_device_lists( DBusMessage *reply, struct list *adapter_list )
{
DBusMessageIter dict, paths_iter, iface_iter, prop_iter;
const char *path;
NTSTATUS status = STATUS_SUCCESS;
if (!p_dbus_message_has_signature( reply,
DBUS_OBJECTMANAGER_METHOD_GETMANAGEDOBJECTS_RETURN_SIGNATURE ))
{
ERR( "Unexpected signature in GetManagedObjects reply: %s\n",
debugstr_a( p_dbus_message_get_signature( reply ) ) );
return STATUS_INTERNAL_ERROR;
}
p_dbus_message_iter_init( reply, &dict );
p_dbus_message_iter_recurse( &dict, &paths_iter );
while((path = bluez_next_dict_entry( &paths_iter, &iface_iter )))
{
const char *iface;
while ((iface = bluez_next_dict_entry ( &iface_iter, &prop_iter )))
{
if (!strcmp( iface, BLUEZ_INTERFACE_ADAPTER ))
{
struct bluez_init_entry *init_device = calloc( 1, sizeof( *init_device ) );
struct unix_name *radio_name;
if (!init_device)
{
status = STATUS_NO_MEMORY;
goto done;
}
radio_name = unix_name_get_or_create( path );
if (!radio_name)
{
free( init_device );
status = STATUS_NO_MEMORY;
goto done;
}
init_device->object.radio.radio.handle = (UINT_PTR)radio_name;
list_add_tail( adapter_list, &init_device->entry );
TRACE( "Found BlueZ org.bluez.Adapter1 object %s: %p\n",
debugstr_a( radio_name->str ), radio_name );
break;
}
}
}
TRACE( "Initial device list: radios: %d\n", list_count( adapter_list ) );
done:
return status;
}
static struct bluez_init_entry *bluez_init_entries_list_pop( struct list *list )
{
struct list *entry = list_head( list );
struct bluez_init_entry *device = LIST_ENTRY( entry, struct bluez_init_entry, entry );
list_remove( entry );
return device;
}
NTSTATUS bluez_dbus_loop( void *c, void *watcher,
struct winebluetooth_event *result )
{
DBusConnection *connection;
struct bluez_watcher_ctx *watcher_ctx = watcher;
TRACE( "(%p, %p, %p)\n", c, watcher, result );
connection = p_dbus_connection_ref( c );
while (TRUE)
{
if (!list_empty( &watcher_ctx->initial_radio_list ))
{
struct bluez_init_entry *radio =
bluez_init_entries_list_pop( &watcher_ctx->initial_radio_list );
struct winebluetooth_watcher_event *watcher_event = &result->data.watcher_event;
result->status = WINEBLUETOOTH_EVENT_WATCHER_EVENT;
watcher_event->event_type = BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED;
watcher_event->event_data.radio_added = radio->object.radio;
free( radio );
p_dbus_connection_unref( connection );
return STATUS_PENDING;
}
else if (!p_dbus_connection_read_write_dispatch( connection, 100 ))
{
p_dbus_connection_unref( connection );
TRACE( "Disconnected from DBus\n" );
return STATUS_SUCCESS;
}
if (watcher_ctx->init_device_list_call != NULL
&& p_dbus_pending_call_get_completed( watcher_ctx->init_device_list_call ))
{
DBusMessage *reply = p_dbus_pending_call_steal_reply( watcher_ctx->init_device_list_call );
DBusError error;
NTSTATUS status;
p_dbus_pending_call_unref( watcher_ctx->init_device_list_call );
watcher_ctx->init_device_list_call = NULL;
p_dbus_error_init( &error );
if (p_dbus_set_error_from_message( &error, reply ))
{
ERR( "Error getting object list from BlueZ: '%s': '%s'\n", error.name,
error.message );
p_dbus_error_free( &error );
p_dbus_message_unref( reply );
p_dbus_connection_unref( connection );
return STATUS_NO_MEMORY;
}
status = bluez_build_initial_device_lists( reply, &watcher_ctx->initial_radio_list );
p_dbus_message_unref( reply );
if (status != STATUS_SUCCESS)
{
ERR( "Error building initial bluetooth devices list: %#x\n", (int)status );
p_dbus_connection_unref( connection );
return status;
}
}
}
}
#else
void *bluez_dbus_init( void ) { return NULL; }
void bluez_dbus_close( void *connection ) {}
void bluez_dbus_free( void *connection ) {}
NTSTATUS bluez_watcher_init( void *connection, void **ctx ) { return STATUS_NOT_SUPPORTED; }
NTSTATUS bluez_dbus_loop( void *c, void *watcher, struct winebluetooth_event *result )
{
return STATUS_NOT_SUPPORTED;
}
#endif /* SONAME_LIBDBUS_1 */

113
dlls/winebth.sys/dbus.h Normal file
View file

@ -0,0 +1,113 @@
/*
* DBus declarations.
*
* Copyright 2024 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
*/
#ifndef __WINE_BLUETOOTHAPIS_UNIXLIB_DBUS_H
#define __WINE_BLUETOOTHAPIS_UNIXLIB_DBUS_H
#include "config.h"
#ifdef SONAME_LIBDBUS_1
#include <dbus/dbus.h>
#endif
#ifdef SONAME_LIBDBUS_1
#define DBUS_FUNCS \
DO_FUNC(dbus_bus_add_match); \
DO_FUNC(dbus_bus_get); \
DO_FUNC(dbus_bus_get_id); \
DO_FUNC(dbus_bus_get_private); \
DO_FUNC(dbus_bus_remove_match); \
DO_FUNC(dbus_connection_add_filter); \
DO_FUNC(dbus_connection_close); \
DO_FUNC(dbus_connection_flush); \
DO_FUNC(dbus_connection_free_preallocated_send); \
DO_FUNC(dbus_connection_get_is_anonymous); \
DO_FUNC(dbus_connection_get_is_authenticated); \
DO_FUNC(dbus_connection_get_is_connected); \
DO_FUNC(dbus_connection_get_server_id); \
DO_FUNC(dbus_connection_get_unix_process_id); \
DO_FUNC(dbus_connection_get_unix_fd); \
DO_FUNC(dbus_connection_get_unix_user); \
DO_FUNC(dbus_connection_preallocate_send); \
DO_FUNC(dbus_connection_read_write_dispatch); \
DO_FUNC(dbus_connection_remove_filter); \
DO_FUNC(dbus_connection_ref); \
DO_FUNC(dbus_connection_send); \
DO_FUNC(dbus_connection_send_preallocated); \
DO_FUNC(dbus_connection_send_with_reply); \
DO_FUNC(dbus_connection_send_with_reply_and_block); \
DO_FUNC(dbus_connection_try_register_object_path); \
DO_FUNC(dbus_connection_unref); \
DO_FUNC(dbus_connection_unregister_object_path); \
DO_FUNC(dbus_error_free); \
DO_FUNC(dbus_error_has_name) ; \
DO_FUNC(dbus_error_init); \
DO_FUNC(dbus_error_is_set); \
DO_FUNC(dbus_free); \
DO_FUNC(dbus_free_string_array); \
DO_FUNC(dbus_message_append_args); \
DO_FUNC(dbus_message_get_args); \
DO_FUNC(dbus_message_iter_get_element_count); \
DO_FUNC(dbus_message_get_interface); \
DO_FUNC(dbus_message_get_member); \
DO_FUNC(dbus_message_get_path); \
DO_FUNC(dbus_message_get_sender); \
DO_FUNC(dbus_message_get_serial); \
DO_FUNC(dbus_message_get_signature); \
DO_FUNC(dbus_message_get_type); \
DO_FUNC(dbus_message_has_signature); \
DO_FUNC(dbus_message_iter_has_next); \
DO_FUNC(dbus_message_is_error); \
DO_FUNC(dbus_message_is_method_call); \
DO_FUNC(dbus_message_is_signal); \
DO_FUNC(dbus_message_iter_append_basic); \
DO_FUNC(dbus_message_iter_close_container); \
DO_FUNC(dbus_message_iter_get_arg_type); \
DO_FUNC(dbus_message_iter_get_element_type); \
DO_FUNC(dbus_message_iter_get_basic); \
DO_FUNC(dbus_message_iter_get_fixed_array); \
DO_FUNC(dbus_message_iter_get_signature); \
DO_FUNC(dbus_message_iter_init); \
DO_FUNC(dbus_message_iter_init_append); \
DO_FUNC(dbus_message_iter_next); \
DO_FUNC(dbus_message_iter_open_container); \
DO_FUNC(dbus_message_iter_recurse); \
DO_FUNC(dbus_message_new_error); \
DO_FUNC(dbus_message_new_error_printf); \
DO_FUNC(dbus_message_new_method_return); \
DO_FUNC(dbus_message_new_method_call); \
DO_FUNC(dbus_message_ref); \
DO_FUNC(dbus_message_unref); \
DO_FUNC(dbus_pending_call_block); \
DO_FUNC(dbus_pending_call_cancel); \
DO_FUNC(dbus_pending_call_get_completed); \
DO_FUNC(dbus_pending_call_set_notify); \
DO_FUNC(dbus_pending_call_steal_reply); \
DO_FUNC(dbus_pending_call_unref); \
DO_FUNC(dbus_set_error_from_message); \
DO_FUNC(dbus_threads_init_default);
#define DO_FUNC( f ) extern typeof( f ) *p_##f
DBUS_FUNCS;
#undef DO_FUNC
#endif /* SONAME_LIBDBUS_1 */
#endif /* __WINE_BLUETOOTHAPIS_UNIXLIB_DBUS_H */

176
dlls/winebth.sys/unixlib.c Normal file
View file

@ -0,0 +1,176 @@
/*
* winebluetooth Unix interface
*
* Copyright 2024 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
*/
#if 0
#pragma makedep unix
#endif
#include <config.h>
#include <stdlib.h>
#include <stdarg.h>
#include <assert.h>
#include <pthread.h>
#include <ntstatus.h>
#define WIN32_NO_STATUS
#include <winternl.h>
#include <winbase.h>
#include <windef.h>
#include <wine/list.h>
#include <wine/rbtree.h>
#include <wine/debug.h>
#include "unixlib.h"
#include "unixlib_priv.h"
WINE_DEFAULT_DEBUG_CHANNEL( winebth );
static int compare_string( const void *key, const struct wine_rb_entry *entry )
{
struct unix_name *str = WINE_RB_ENTRY_VALUE( entry, struct unix_name, entry );
return strcmp( key, str->str );
}
static struct rb_tree names = { .compare = compare_string };
static pthread_mutex_t names_mutex = PTHREAD_MUTEX_INITIALIZER;
struct unix_name *unix_name_get_or_create( const char *str )
{
struct rb_entry *entry;
struct unix_name *s;
pthread_mutex_lock( &names_mutex );
entry = rb_get( &names, str );
if (!entry)
{
struct unix_name *s = malloc( sizeof( struct unix_name ) );
if (!s)
{
pthread_mutex_unlock( &names_mutex );
return NULL;
}
s->str = strdup( str );
s->refcnt = 0;
rb_put( &names, str, &s->entry );
entry = &s->entry;
}
s = RB_ENTRY_VALUE( entry, struct unix_name, entry );
s->refcnt++;
pthread_mutex_unlock( &names_mutex );
return s;
}
void unix_name_free( struct unix_name *name )
{
pthread_mutex_lock( &names_mutex );
name->refcnt--;
if (name->refcnt == 0)
{
rb_remove( &names, &name->entry );
free( name );
}
pthread_mutex_unlock( &names_mutex );
}
static void *dbus_connection;
static void *bluetooth_watcher;
static NTSTATUS bluetooth_init ( void *params )
{
NTSTATUS status;
dbus_connection = bluez_dbus_init();
if (!dbus_connection)
return STATUS_INTERNAL_ERROR;
status = bluez_watcher_init( dbus_connection, &bluetooth_watcher );
if (status)
bluez_dbus_close( dbus_connection );
else
TRACE( "dbus_connection=%p bluetooth_watcher=%p\n", dbus_connection, bluetooth_watcher );
return status;
}
static NTSTATUS bluetooth_shutdown( void *params )
{
if (!dbus_connection) return STATUS_NOT_SUPPORTED;
bluez_dbus_close( dbus_connection );
bluez_dbus_free( dbus_connection );
return STATUS_SUCCESS;
}
static NTSTATUS get_unique_name( const struct unix_name *name, char *buf, SIZE_T *buf_size )
{
SIZE_T path_len, i;
path_len = strlen( name->str );
if (*buf_size <= (path_len * sizeof(char)))
{
*buf_size = (path_len + 1) * sizeof(char);
return STATUS_BUFFER_TOO_SMALL;
}
for (i = 0; i < path_len; i++)
{
if (name->str[i] == '/') buf[i] = '_';
else
buf[i] = name->str[i];
}
buf[path_len] = '\0';
return STATUS_SUCCESS;
}
static NTSTATUS bluetooth_adapter_get_unique_name( void *args )
{
struct bluetooth_adapter_get_unique_name_params *params = args;
if (!dbus_connection) return STATUS_NOT_SUPPORTED;
return get_unique_name( params->adapter, params->buf, &params->buf_size );
}
static NTSTATUS bluetooth_adapter_free( void *args )
{
struct bluetooth_adapter_free_params *params = args;
unix_name_free( params->adapter );
return STATUS_SUCCESS;
}
static NTSTATUS bluetooth_get_event( void *args )
{
struct bluetooth_get_event_params *params = args;
if (!dbus_connection) return STATUS_NOT_SUPPORTED;
memset( &params->result, 0, sizeof( params->result ) );
return bluez_dbus_loop( dbus_connection, bluetooth_watcher, &params->result );
}
const unixlib_entry_t __wine_unix_call_funcs[] = {
bluetooth_init,
bluetooth_shutdown,
bluetooth_adapter_get_unique_name,
bluetooth_adapter_free,
bluetooth_get_event,
};
C_ASSERT( ARRAYSIZE( __wine_unix_call_funcs ) == unix_funcs_count );

View file

@ -0,0 +1,79 @@
/*
* Unix interface definitions
*
* Copyright 2024 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
*/
#ifndef __WINE_WINEBTH_UNIXLIB_H
#define __WINE_WINEBTH_UNIXLIB_H
#include <stdlib.h>
#include <stdarg.h>
#include <windef.h>
#include <winbase.h>
#include <bthsdpdef.h>
#include <bluetoothapis.h>
#include <wine/unixlib.h>
#include <wine/debug.h>
#include "winebth_priv.h"
#ifdef WINE_UNIX_LIB
typedef struct unix_name *unix_name_t;
typedef void *unix_handle_t;
#else
typedef UINT_PTR unix_name_t;
typedef UINT_PTR unix_handle_t;
#endif
struct bluetooth_adapter_free_params
{
unix_name_t adapter;
};
struct bluetooth_adapter_get_unique_name_params
{
unix_name_t adapter;
char *buf;
SIZE_T buf_size;
};
struct bluetooth_get_event_params
{
struct winebluetooth_event result;
};
enum bluetoothapis_funcs
{
unix_bluetooth_init,
unix_bluetooth_shutdown,
unix_bluetooth_adapter_get_unique_name,
unix_bluetooth_adapter_free,
unix_bluetooth_get_event,
unix_funcs_count
};
#define UNIX_BLUETOOTH_CALL( func, params ) WINE_UNIX_CALL( unix_##func, params )
#endif /* __WINE_WINEBTH_UNIXLIB_H */

View file

@ -0,0 +1,51 @@
/*
* Bluetoothapis Unix interface
*
* Copyright 2024 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
*/
#ifndef __WINE_WINEBTH_UNIXLIB_PRIV_H
#define __WINE_WINEBTH_UNIXLIB_PRIV_H
#include <config.h>
#include <ntstatus.h>
#define WIN32_NO_STATUS
#include <windef.h>
#include <wine/list.h>
#include <wine/rbtree.h>
#include "unixlib.h"
struct unix_name
{
char *str;
SIZE_T refcnt;
struct wine_rb_entry entry;
};
extern struct unix_name *unix_name_get_or_create( const char *str );
extern void unix_name_free( struct unix_name *name );
extern void *bluez_dbus_init( void );
extern void bluez_dbus_close( void *connection );
extern void bluez_dbus_free( void *connection );
extern NTSTATUS bluez_dbus_loop( void *connection, void *watcher_ctx, struct winebluetooth_event *result );
extern NTSTATUS bluez_watcher_init( void *connection, void **ctx );
#endif /* __WINE_WINEBTH_UNIXLIB_PRIV_H */

View file

@ -0,0 +1,91 @@
/*
* Wine bluetooth APIs
*
* Copyright 2024 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 <ntstatus.h>
#define WIN32_NO_STATUS
#include <windef.h>
#include <winbase.h>
#include <wine/debug.h>
#include <wine/heap.h>
#include <wine/unixlib.h>
#include "winebth_priv.h"
#include "unixlib.h"
WINE_DEFAULT_DEBUG_CHANNEL( winebth );
NTSTATUS winebluetooth_radio_get_unique_name( winebluetooth_radio_t radio, char *name,
SIZE_T *size )
{
struct bluetooth_adapter_get_unique_name_params params = {0};
NTSTATUS status;
TRACE( "(%p, %p, %p)\n", (void *)radio.handle, name, size );
params.adapter = radio.handle;
params.buf = name;
params.buf_size = *size;
status = UNIX_BLUETOOTH_CALL( bluetooth_adapter_get_unique_name, &params );
if (status == STATUS_BUFFER_TOO_SMALL)
*size = params.buf_size;
return status;
}
void winebluetooth_radio_free( winebluetooth_radio_t radio )
{
struct bluetooth_adapter_free_params args = { 0 };
TRACE( "(%p)\n", (void *)radio.handle );
args.adapter = radio.handle;
UNIX_BLUETOOTH_CALL( bluetooth_adapter_free, &args );
}
NTSTATUS winebluetooth_get_event( struct winebluetooth_event *result )
{
struct bluetooth_get_event_params params = {0};
NTSTATUS status;
TRACE( "(%p)\n", result );
status = UNIX_BLUETOOTH_CALL( bluetooth_get_event, &params );
*result = params.result;
return status;
}
NTSTATUS winebluetooth_init( void )
{
NTSTATUS status;
status = __wine_init_unix_call();
if (status != STATUS_SUCCESS)
return status;
return UNIX_BLUETOOTH_CALL( bluetooth_init, NULL );
}
NTSTATUS winebluetooth_shutdown( void )
{
return UNIX_BLUETOOTH_CALL( bluetooth_shutdown, NULL );
}

442
dlls/winebth.sys/winebth.c Normal file
View file

@ -0,0 +1,442 @@
/*
* Bluetooth bus driver
*
* Copyright 2024 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 <stdlib.h>
#include <assert.h>
#include <ntstatus.h>
#define WIN32_NO_STATUS
#include <windef.h>
#include <winbase.h>
#include <winternl.h>
#include <initguid.h>
#include <devpkey.h>
#include <bthdef.h>
#include <winioctl.h>
#include <ddk/wdm.h>
#include <wine/debug.h>
#include <wine/list.h>
#include "winebth_priv.h"
WINE_DEFAULT_DEBUG_CHANNEL( winebth );
static DRIVER_OBJECT *driver_obj;
static DEVICE_OBJECT *bus_fdo, *bus_pdo;
#define DECLARE_CRITICAL_SECTION( cs ) \
static CRITICAL_SECTION cs; \
static CRITICAL_SECTION_DEBUG cs##_debug = { \
0, \
0, \
&( cs ), \
{ &cs##_debug.ProcessLocksList, &cs##_debug.ProcessLocksList }, \
0, \
0, \
{ (DWORD_PTR)( __FILE__ ": " #cs ) } }; \
static CRITICAL_SECTION cs = { &cs##_debug, -1, 0, 0, 0, 0 };
DECLARE_CRITICAL_SECTION( device_list_cs );
static struct list device_list = LIST_INIT( device_list );
struct bluetooth_radio
{
struct list entry;
BOOL removed;
DEVICE_OBJECT *device_obj;
winebluetooth_radio_t radio;
WCHAR *hw_name;
UNICODE_STRING bthport_symlink_name;
UNICODE_STRING bthradio_symlink_name;
};
void WINAPIV append_id( struct string_buffer *buffer, const WCHAR *format, ... )
{
va_list args;
WCHAR *string;
int len;
va_start( args, format );
len = _vsnwprintf( NULL, 0, format, args ) + 1;
if (!(string = ExAllocatePool( PagedPool, (buffer->len + len) * sizeof( WCHAR ) )))
{
if (buffer->string)
ExFreePool( buffer->string );
buffer->string = NULL;
return;
}
if (buffer->string)
{
memcpy( string, buffer->string, buffer->len * sizeof( WCHAR ) );
ExFreePool( buffer->string );
}
_vsnwprintf( string + buffer->len, len, format, args );
buffer->string = string;
buffer->len += len;
va_end( args );
}
static HANDLE event_loop_thread;
static NTSTATUS radio_get_hw_name_w( winebluetooth_radio_t radio, WCHAR **name )
{
char *name_a;
SIZE_T size = sizeof( char ) * 256;
NTSTATUS status;
name_a = malloc( size );
if (!name_a)
return STATUS_NO_MEMORY;
status = winebluetooth_radio_get_unique_name( radio, name_a, &size );
if (status == STATUS_BUFFER_TOO_SMALL)
{
void *ptr = realloc( name_a, size );
if (!ptr)
{
free( name_a );
return STATUS_NO_MEMORY;
}
name_a = ptr;
status = winebluetooth_radio_get_unique_name( radio, name_a, &size );
}
if (status != STATUS_SUCCESS)
{
free( name_a );
return status;
}
*name = malloc( (mbstowcs( NULL, name_a, 0 ) + 1) * sizeof( WCHAR ));
if (!*name)
{
free( name_a );
return status;
}
mbstowcs( *name, name_a, strlen( name_a ) + 1 );
free( name_a );
return STATUS_SUCCESS;
}
static void add_bluetooth_radio( struct winebluetooth_watcher_event_radio_added event )
{
struct bluetooth_radio *device;
DEVICE_OBJECT *device_obj;
UNICODE_STRING string;
NTSTATUS status;
WCHAR name[256];
WCHAR *hw_name;
static unsigned int radio_index;
swprintf( name, ARRAY_SIZE( name ), L"\\Device\\WINEBTH-RADIO-%d", radio_index++ );
TRACE( "Adding new bluetooth radio %p: %s\n", (void *)event.radio.handle, debugstr_w( name ) );
status = radio_get_hw_name_w( event.radio, &hw_name );
if (status)
{
ERR( "Failed to get hardware name for radio %p, status %#lx\n", (void *)event.radio.handle, status );
return;
}
RtlInitUnicodeString( &string, name );
status = IoCreateDevice( driver_obj, sizeof( *device ), &string, FILE_DEVICE_BLUETOOTH, 0,
FALSE, &device_obj );
if (status)
{
ERR( "Failed to create device, status %#lx\n", status );
return;
}
device = device_obj->DeviceExtension;
device->device_obj = device_obj;
device->radio = event.radio;
device->removed = FALSE;
device->hw_name = hw_name;
EnterCriticalSection( &device_list_cs );
list_add_tail( &device_list, &device->entry );
LeaveCriticalSection( &device_list_cs );
IoInvalidateDeviceRelations( bus_pdo, BusRelations );
}
static DWORD CALLBACK bluetooth_event_loop_thread_proc( void *arg )
{
NTSTATUS status;
while (TRUE)
{
struct winebluetooth_event result = {0};
status = winebluetooth_get_event( &result );
if (status != STATUS_PENDING) break;
switch (result.status)
{
case WINEBLUETOOTH_EVENT_WATCHER_EVENT:
{
struct winebluetooth_watcher_event *event = &result.data.watcher_event;
switch (event->event_type)
{
case BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED:
add_bluetooth_radio( event->event_data.radio_added );
break;
default:
FIXME( "Unknown bluetooth watcher event code: %#x\n", event->event_type );
}
break;
}
default:
FIXME( "Unknown bluetooth event loop status code: %#x\n", result.status );
}
}
if (status != STATUS_SUCCESS)
ERR( "Bluetooth event loop terminated with %#lx", status );
else
TRACE( "Exiting bluetooth event loop\n" );
return 0;
}
static NTSTATUS WINAPI fdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp )
{
IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp );
TRACE( "irp %p, minor function %s.\n", irp, debugstr_minor_function_code( stack->MinorFunction ) );
switch (stack->MinorFunction)
{
case IRP_MN_QUERY_DEVICE_RELATIONS:
{
struct bluetooth_radio *radio;
DEVICE_RELATIONS *devices;
SIZE_T i = 0;
if (stack->Parameters.QueryDeviceRelations.Type != BusRelations)
{
FIXME( "Unhandled Device Relation %x\n",
stack->Parameters.QueryDeviceRelations.Type );
break;
}
EnterCriticalSection( &device_list_cs );
devices = ExAllocatePool(
PagedPool, offsetof( DEVICE_RELATIONS, Objects[list_count( &device_list )] ) );
if (devices == NULL)
{
LeaveCriticalSection( &device_list_cs );
irp->IoStatus.Status = STATUS_NO_MEMORY;
break;
}
LIST_FOR_EACH_ENTRY(radio, &device_list, struct bluetooth_radio, entry)
{
devices->Objects[i++] = radio->device_obj;
call_fastcall_func1( ObfReferenceObject, radio->device_obj );
}
LeaveCriticalSection( &device_list_cs );
devices->Count = i;
irp->IoStatus.Information = (ULONG_PTR)devices;
irp->IoStatus.Status = STATUS_SUCCESS;
break;
}
case IRP_MN_START_DEVICE:
event_loop_thread =
CreateThread( NULL, 0, bluetooth_event_loop_thread_proc, NULL, 0, NULL );
irp->IoStatus.Status = STATUS_SUCCESS;
break;
case IRP_MN_SURPRISE_REMOVAL:
irp->IoStatus.Status = STATUS_SUCCESS;
break;
case IRP_MN_REMOVE_DEVICE:
{
struct bluetooth_radio *device, *cur;
NTSTATUS ret;
winebluetooth_shutdown();
WaitForSingleObject( event_loop_thread, INFINITE );
CloseHandle( event_loop_thread );
EnterCriticalSection( &device_list_cs );
LIST_FOR_EACH_ENTRY_SAFE( device, cur, &device_list, struct bluetooth_radio, entry )
{
assert( !device->removed );
winebluetooth_radio_free( device->radio );
list_remove( &device->entry );
IoDeleteDevice( device->device_obj );
}
LeaveCriticalSection( &device_list_cs );
IoSkipCurrentIrpStackLocation( irp );
ret = IoCallDriver( bus_pdo, irp );
IoDetachDevice( bus_pdo );
IoDeleteDevice( bus_fdo );
return ret;
}
default:
FIXME( "Unhandled minor function %s.\n", debugstr_minor_function_code( stack->MinorFunction ) );
}
IoSkipCurrentIrpStackLocation( irp );
return IoCallDriver( bus_pdo, irp );
}
static NTSTATUS query_id(const struct bluetooth_radio *ext, IRP *irp, BUS_QUERY_ID_TYPE type )
{
struct string_buffer buf = {0};
TRACE( "(%p, %p, %s)\n", ext, irp, debugstr_BUS_QUERY_ID_TYPE( type ) );
switch (type)
{
case BusQueryDeviceID:
append_id( &buf, L"WINEBTH\\RADIO" );
break;
case BusQueryInstanceID:
append_id( &buf, L"%s", ext->hw_name );
break;
case BusQueryHardwareIDs:
case BusQueryCompatibleIDs:
append_id( &buf, L"" );
break;
default:
return irp->IoStatus.Status;
}
if (!buf.string)
return STATUS_NO_MEMORY;
irp->IoStatus.Information = (ULONG_PTR)buf.string;
return STATUS_SUCCESS;
}
static NTSTATUS WINAPI pdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp )
{
IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp);
struct bluetooth_radio *device = device_obj->DeviceExtension;
NTSTATUS ret = irp->IoStatus.Status;
TRACE( "device_obj %p, irp %p, minor function %s\n", device_obj, irp, debugstr_minor_function_code( stack->MinorFunction ) );
switch (stack->MinorFunction)
{
case IRP_MN_QUERY_ID:
ret = query_id( device, irp, stack->Parameters.QueryId.IdType );
break;
case IRP_MN_QUERY_CAPABILITIES:
{
DEVICE_CAPABILITIES *caps = stack->Parameters.DeviceCapabilities.Capabilities;
caps->Removable = TRUE;
caps->SurpriseRemovalOK = TRUE;
caps->RawDeviceOK = TRUE;
ret = STATUS_SUCCESS;
break;
}
case IRP_MN_START_DEVICE:
if (IoRegisterDeviceInterface( device_obj, &GUID_BTHPORT_DEVICE_INTERFACE, NULL,
&device->bthport_symlink_name ) == STATUS_SUCCESS)
IoSetDeviceInterfaceState( &device->bthport_symlink_name, TRUE );
if (IoRegisterDeviceInterface( device_obj, &GUID_BLUETOOTH_RADIO_INTERFACE, NULL,
&device->bthradio_symlink_name ) == STATUS_SUCCESS)
IoSetDeviceInterfaceState( &device->bthradio_symlink_name, TRUE );
ret = STATUS_SUCCESS;
break;
case IRP_MN_REMOVE_DEVICE:
assert( device->removed );
if (device->bthport_symlink_name.Buffer)
{
IoSetDeviceInterfaceState(&device->bthport_symlink_name, FALSE);
RtlFreeUnicodeString( &device->bthport_symlink_name );
}
if (device->bthradio_symlink_name.Buffer)
{
IoSetDeviceInterfaceState(&device->bthradio_symlink_name, FALSE);
RtlFreeUnicodeString( &device->bthradio_symlink_name );
}
free( device->hw_name );
winebluetooth_radio_free( device->radio );
IoDeleteDevice( device->device_obj );
ret = STATUS_SUCCESS;
break;
case IRP_MN_SURPRISE_REMOVAL:
EnterCriticalSection( &device_list_cs );
if (!device->removed)
{
device->removed = TRUE;
list_remove( &device->entry );
}
LeaveCriticalSection( &device_list_cs );
ret = STATUS_SUCCESS;
break;
default:
FIXME( "Unhandled minor function %s.\n", debugstr_minor_function_code( stack->MinorFunction ) );
break;
}
irp->IoStatus.Status = ret;
IoCompleteRequest( irp, IO_NO_INCREMENT );
return ret;
}
static NTSTATUS WINAPI bluetooth_pnp( DEVICE_OBJECT *device, IRP *irp )
{
if (device == bus_fdo)
return fdo_pnp( device, irp );
return pdo_pnp( device, irp );
}
static NTSTATUS WINAPI driver_add_device( DRIVER_OBJECT *driver, DEVICE_OBJECT *pdo )
{
NTSTATUS ret;
TRACE( "(%p, %p)\n", driver, pdo );
ret = IoCreateDevice( driver, 0, NULL, FILE_DEVICE_BUS_EXTENDER, 0, FALSE, &bus_fdo );
if (ret != STATUS_SUCCESS)
{
ERR( "failed to create FDO: %#lx\n", ret );
return ret;
}
IoAttachDeviceToDeviceStack( bus_fdo, pdo );
bus_pdo = pdo;
bus_fdo->Flags &= ~DO_DEVICE_INITIALIZING;
return STATUS_SUCCESS;
}
static void WINAPI driver_unload( DRIVER_OBJECT *driver ) {}
NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path )
{
NTSTATUS status;
TRACE( "(%p, %s)\n", driver, debugstr_w( path->Buffer ) );
status = winebluetooth_init();
if (status)
return status;
driver_obj = driver;
driver->DriverExtension->AddDevice = driver_add_device;
driver->DriverUnload = driver_unload;
driver->MajorFunction[IRP_MJ_PNP] = bluetooth_pnp;
return STATUS_SUCCESS;
}

View file

@ -0,0 +1,22 @@
[Version]
Signature="$CHICAGO$"
ClassGuid={4d36e97d-e325-11ce-bfc1-08002be10318}
Class=System
[Manufacturer]
Wine=mfg_section
[mfg_section]
Wine Bluetooth bus driver=device_section,root\winebth
[device_section.Services]
AddService = winebth,0x2,svc_section
[svc_section]
Description="Wine Bluetooth bus driver"
DisplayName="Wine Bluetooth bus"
ServiceBinary="%12%\winebth.sys"
LoadOrderGroup="WinePlugPlay"
ServiceType=1
StartType=3
ErrorControl=1

View file

@ -0,0 +1,20 @@
/*
* Copyright 2024 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
*/
/* @makedep: winebth.inf */
1 WINE_DATA_FILE winebth.inf

View file

@ -0,0 +1,146 @@
/*
* Private winebth.sys defs
*
* Copyright 2024 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
*/
#ifndef __WINE_WINEBTH_WINEBTH_H_
#define __WINE_WINEBTH_WINEBTH_H_
#include <bthsdpdef.h>
#include <bluetoothapis.h>
#include <ddk/wdm.h>
#include <wine/debug.h>
#ifdef __ASM_USE_FASTCALL_WRAPPER
extern void * WINAPI wrap_fastcall_func1(void *func, const void *a);
__ASM_STDCALL_FUNC(wrap_fastcall_func1, 8,
"popl %ecx\n\t"
"popl %eax\n\t"
"xchgl (%esp),%ecx\n\t"
"jmp *%eax");
#define call_fastcall_func1(func,a) wrap_fastcall_func1(func,a)
#else
#define call_fastcall_func1(func,a) func(a)
#endif
struct string_buffer
{
WCHAR *string;
size_t len;
};
#define XX(i) case (i): return #i
static inline const char *debugstr_BUS_QUERY_ID_TYPE( BUS_QUERY_ID_TYPE type )
{
switch (type)
{
XX(BusQueryDeviceID);
XX(BusQueryHardwareIDs);
XX(BusQueryCompatibleIDs);
XX(BusQueryInstanceID);
XX(BusQueryDeviceSerialNumber);
XX(BusQueryContainerID);
default:
return wine_dbg_sprintf( "(unknown %d)", type );
}
}
static inline const char *debugstr_minor_function_code( UCHAR code )
{
switch(code)
{
XX(IRP_MN_START_DEVICE);
XX(IRP_MN_QUERY_REMOVE_DEVICE);
XX(IRP_MN_REMOVE_DEVICE);
XX(IRP_MN_CANCEL_REMOVE_DEVICE);
XX(IRP_MN_STOP_DEVICE);
XX(IRP_MN_QUERY_STOP_DEVICE);
XX(IRP_MN_CANCEL_STOP_DEVICE);
XX(IRP_MN_QUERY_DEVICE_RELATIONS);
XX(IRP_MN_QUERY_INTERFACE);
XX(IRP_MN_QUERY_CAPABILITIES);
XX(IRP_MN_QUERY_RESOURCES);
XX(IRP_MN_QUERY_RESOURCE_REQUIREMENTS);
XX(IRP_MN_QUERY_DEVICE_TEXT);
XX(IRP_MN_FILTER_RESOURCE_REQUIREMENTS);
XX(IRP_MN_READ_CONFIG);
XX(IRP_MN_WRITE_CONFIG);
XX(IRP_MN_EJECT);
XX(IRP_MN_SET_LOCK);
XX(IRP_MN_QUERY_ID);
XX(IRP_MN_QUERY_PNP_DEVICE_STATE);
XX(IRP_MN_QUERY_BUS_INFORMATION);
XX(IRP_MN_DEVICE_USAGE_NOTIFICATION);
XX(IRP_MN_SURPRISE_REMOVAL);
XX(IRP_MN_QUERY_LEGACY_BUS_INFORMATION);
default:
return wine_dbg_sprintf( "(unknown %#x)", code );
}
}
#undef XX
typedef struct
{
UINT_PTR handle;
} winebluetooth_radio_t;
NTSTATUS winebluetooth_radio_get_unique_name( winebluetooth_radio_t radio, char *name,
SIZE_T *size );
void winebluetooth_radio_free( winebluetooth_radio_t radio );
enum winebluetooth_watcher_event_type
{
BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED,
};
struct winebluetooth_watcher_event_radio_added
{
winebluetooth_radio_t radio;
};
union winebluetooth_watcher_event_data
{
struct winebluetooth_watcher_event_radio_added radio_added;
};
struct winebluetooth_watcher_event
{
enum winebluetooth_watcher_event_type event_type;
union winebluetooth_watcher_event_data event_data;
};
enum winebluetooth_event_type
{
WINEBLUETOOTH_EVENT_WATCHER_EVENT,
};
struct winebluetooth_event
{
enum winebluetooth_event_type status;
union {
struct winebluetooth_watcher_event watcher_event;
} data;
};
NTSTATUS winebluetooth_get_event( struct winebluetooth_event *result );
NTSTATUS winebluetooth_init( void );
NTSTATUS winebluetooth_shutdown( void );
#endif /* __WINE_WINEBTH_WINEBTH_H_ */

View file

@ -58,6 +58,7 @@ SOURCES = \
bits5_0.idl \
bitsmsg.h \
bluetoothapis.h \
bthdef.h \
bthsdpdef.h \
cderr.h \
cdosys.idl \

36
include/bthdef.h Normal file
View file

@ -0,0 +1,36 @@
/*
* Copyright (C) 2024 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
*/
#ifndef __BTHDEF_H__
#define __BTHDEF_H__
#ifdef __cplusplus
extern "C" {
#endif
DEFINE_GUID( GUID_BTHPORT_DEVICE_INTERFACE, 0x850302a, 0xb344, 0x4fda, 0x9b, 0xe9, 0x90, 0x57, 0x6b,
0x8d, 0x46, 0xf0 );
DEFINE_GUID( GUID_BLUETOOTH_RADIO_INTERFACE, 0x92383b0e, 0xf90e, 0x4ac9, 0x8d, 0x44, 0x8c, 0x2d,
0x0d, 0x0e, 0xbd, 0xa2 );
#ifdef __cplusplus
}
#endif
#endif /* __BTHDEF_H__ */

View file

@ -2629,6 +2629,7 @@ services,"@%11%\ws2_32.dll,-4"
[InfFiles]
input.inf,"@%12%\hidclass.sys,-1"
mouhid.inf,"@%12%\mouhid.sys,-1"
winebth.inf,"@%12%\winebth.sys,-1"
winebus.inf,"@%12%\winebus.sys,-1"
winehid.inf,"@%12%\winehid.sys,-1"
wineusb.inf,"@%12%\wineusb.sys,-1"

View file

@ -1544,6 +1544,7 @@ static void install_root_pnp_devices(void)
}
root_devices[] =
{
{"root\\wine\\winebth", "root\\winebth\0", "C:\\windows\\inf\\winebth.inf"},
{"root\\wine\\winebus", "root\\winebus\0", "C:\\windows\\inf\\winebus.inf"},
{"root\\wine\\wineusb", "root\\wineusb\0", "C:\\windows\\inf\\wineusb.inf"},
};