mirror of
https://gitlab.winehq.org/wine/wine.git
synced 2024-11-19 17:06:04 -07:00
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:
commit
3248582970
18 changed files with 1567 additions and 0 deletions
|
@ -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
2
configure
generated
vendored
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
13
dlls/winebth.sys/Makefile.in
Normal file
13
dlls/winebth.sys/Makefile.in
Normal 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
367
dlls/winebth.sys/dbus.c
Normal 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
113
dlls/winebth.sys/dbus.h
Normal 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
176
dlls/winebth.sys/unixlib.c
Normal 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, ¶ms->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( ¶ms->result, 0, sizeof( params->result ) );
|
||||
return bluez_dbus_loop( dbus_connection, bluetooth_watcher, ¶ms->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 );
|
79
dlls/winebth.sys/unixlib.h
Normal file
79
dlls/winebth.sys/unixlib.h
Normal 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 */
|
51
dlls/winebth.sys/unixlib_priv.h
Normal file
51
dlls/winebth.sys/unixlib_priv.h
Normal 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 */
|
91
dlls/winebth.sys/winebluetooth.c
Normal file
91
dlls/winebth.sys/winebluetooth.c
Normal 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, ¶ms );
|
||||
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, ¶ms );
|
||||
*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
442
dlls/winebth.sys/winebth.c
Normal 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;
|
||||
}
|
22
dlls/winebth.sys/winebth.inf
Normal file
22
dlls/winebth.sys/winebth.inf
Normal 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
|
20
dlls/winebth.sys/winebth.rc
Normal file
20
dlls/winebth.sys/winebth.rc
Normal 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
|
146
dlls/winebth.sys/winebth_priv.h
Normal file
146
dlls/winebth.sys/winebth_priv.h
Normal 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_ */
|
|
@ -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
36
include/bthdef.h
Normal 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__ */
|
|
@ -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"
|
||||
|
|
|
@ -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"},
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue