mirror of
https://gitlab.winehq.org/wine/wine.git
synced 2024-11-19 17:06:04 -07:00
dlls/winebth.sys: Create radio PDOs from the list of org.bluez.Adapter1 objects on BlueZ.
Add a "bluetooth watcher", which builds a list of radio devices discovered from BlueZ and sends them to the driver in the form of BLUETOOTH_WATCHER_RADIO_DEVICE_ADDED events. Initially, send a list of org.bluez.Adapter1 objects discovered via GetManagedObjects.
This commit is contained in:
parent
983ff4aabf
commit
30ba51e3fc
7 changed files with 666 additions and 8 deletions
|
@ -27,6 +27,8 @@
|
|||
|
||||
#include <stdlib.h>
|
||||
#include <dlfcn.h>
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#ifdef SONAME_LIBDBUS_1
|
||||
#include <dbus/dbus.h>
|
||||
|
@ -37,9 +39,13 @@
|
|||
#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"
|
||||
|
@ -51,6 +57,8 @@ WINE_DECLARE_DEBUG_CHANNEL( dbus );
|
|||
|
||||
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"
|
||||
|
||||
|
@ -79,12 +87,75 @@ failed:
|
|||
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;
|
||||
|
@ -120,10 +191,177 @@ void bluez_dbus_free( void *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 */
|
||||
|
|
|
@ -24,10 +24,18 @@
|
|||
|
||||
#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"
|
||||
|
@ -35,14 +43,70 @@
|
|||
|
||||
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 )
|
||||
{
|
||||
dbus_connection = bluez_dbus_init();
|
||||
TRACE("dbus_connection=%p\n", dbus_connection);
|
||||
NTSTATUS status;
|
||||
|
||||
return dbus_connection ? STATUS_SUCCESS : STATUS_INTERNAL_ERROR;
|
||||
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 )
|
||||
|
@ -54,9 +118,29 @@ static NTSTATUS bluetooth_shutdown( void *params )
|
|||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
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_free,
|
||||
|
||||
bluetooth_get_event,
|
||||
};
|
||||
|
||||
C_ASSERT( ARRAYSIZE( __wine_unix_call_funcs ) == unix_funcs_count );
|
||||
|
|
|
@ -21,15 +21,47 @@
|
|||
#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_get_event_params
|
||||
{
|
||||
struct winebluetooth_event result;
|
||||
};
|
||||
|
||||
enum bluetoothapis_funcs
|
||||
{
|
||||
unix_bluetooth_init,
|
||||
unix_bluetooth_shutdown,
|
||||
|
||||
unix_bluetooth_adapter_free,
|
||||
|
||||
unix_bluetooth_get_event,
|
||||
|
||||
unix_funcs_count
|
||||
};
|
||||
|
||||
|
|
|
@ -32,7 +32,20 @@
|
|||
|
||||
#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 */
|
||||
|
|
|
@ -34,6 +34,29 @@
|
|||
#include "winebth_priv.h"
|
||||
#include "unixlib.h"
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL( winebth );
|
||||
|
||||
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;
|
||||
|
|
|
@ -43,6 +43,132 @@ 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;
|
||||
};
|
||||
|
||||
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 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];
|
||||
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 ) );
|
||||
|
||||
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;
|
||||
|
||||
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 );
|
||||
|
@ -51,7 +177,44 @@ static NTSTATUS WINAPI fdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp )
|
|||
|
||||
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:
|
||||
|
@ -59,8 +222,20 @@ static NTSTATUS WINAPI fdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp )
|
|||
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 );
|
||||
|
@ -75,14 +250,39 @@ static NTSTATUS WINAPI fdo_pnp( DEVICE_OBJECT *device_obj, IRP *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 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;
|
||||
|
@ -96,11 +296,21 @@ static NTSTATUS WINAPI pdo_pnp( DEVICE_OBJECT *device_obj, IRP *irp )
|
|||
ret = STATUS_SUCCESS;
|
||||
break;
|
||||
case IRP_MN_REMOVE_DEVICE:
|
||||
IoDeleteDevice( device_obj );
|
||||
assert( device->removed );
|
||||
winebluetooth_radio_free( device->radio );
|
||||
IoDeleteDevice( device->device_obj );
|
||||
ret = STATUS_SUCCESS;
|
||||
break;
|
||||
case IRP_MN_SURPRISE_REMOVAL:
|
||||
irp->IoStatus.Status = STATUS_SUCCESS;
|
||||
IoCompleteRequest( irp, IO_NO_INCREMENT );
|
||||
return STATUS_SUCCESS;
|
||||
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;
|
||||
|
@ -144,7 +354,7 @@ NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path )
|
|||
TRACE( "(%p, %s)\n", driver, debugstr_w( path->Buffer ) );
|
||||
|
||||
status = winebluetooth_init();
|
||||
if (status != STATUS_SUCCESS)
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
driver_obj = driver;
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#ifndef __WINE_WINEBTH_WINEBTH_H_
|
||||
#define __WINE_WINEBTH_WINEBTH_H_
|
||||
|
||||
#include <bthsdpdef.h>
|
||||
#include <bluetoothapis.h>
|
||||
#include <ddk/wdm.h>
|
||||
|
||||
#include <wine/debug.h>
|
||||
|
@ -44,6 +46,20 @@ struct string_buffer
|
|||
|
||||
#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 )
|
||||
{
|
||||
|
@ -79,6 +95,48 @@ static inline const char *debugstr_minor_function_code( UCHAR code )
|
|||
}
|
||||
#undef XX
|
||||
|
||||
typedef struct
|
||||
{
|
||||
UINT_PTR handle;
|
||||
} winebluetooth_radio_t;
|
||||
|
||||
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 );
|
||||
|
||||
|
|
Loading…
Reference in a new issue