diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index 7840bba161d..01789088ed6 100644 --- a/dlls/winebth.sys/dbus.c +++ b/dlls/winebth.sys/dbus.c @@ -27,6 +27,8 @@ #include #include +#include +#include #ifdef SONAME_LIBDBUS_1 #include @@ -37,9 +39,13 @@ #include #include #include +#include +#include #include +#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 */ diff --git a/dlls/winebth.sys/unixlib.c b/dlls/winebth.sys/unixlib.c index c289a690659..69f3493848f 100644 --- a/dlls/winebth.sys/unixlib.c +++ b/dlls/winebth.sys/unixlib.c @@ -24,10 +24,18 @@ #include +#include #include +#include +#include #include #define WIN32_NO_STATUS +#include +#include +#include +#include +#include #include #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 ); diff --git a/dlls/winebth.sys/unixlib.h b/dlls/winebth.sys/unixlib.h index e79a2c4745a..46509bfb20a 100644 --- a/dlls/winebth.sys/unixlib.h +++ b/dlls/winebth.sys/unixlib.h @@ -21,15 +21,47 @@ #ifndef __WINE_WINEBTH_UNIXLIB_H #define __WINE_WINEBTH_UNIXLIB_H +#include #include +#include +#include + +#include +#include + #include +#include + +#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 }; diff --git a/dlls/winebth.sys/unixlib_priv.h b/dlls/winebth.sys/unixlib_priv.h index e1215f3590a..f74bd6b2e8b 100644 --- a/dlls/winebth.sys/unixlib_priv.h +++ b/dlls/winebth.sys/unixlib_priv.h @@ -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 */ diff --git a/dlls/winebth.sys/winebluetooth.c b/dlls/winebth.sys/winebluetooth.c index 0895311d569..c0088577d13 100644 --- a/dlls/winebth.sys/winebluetooth.c +++ b/dlls/winebth.sys/winebluetooth.c @@ -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; diff --git a/dlls/winebth.sys/winebth.c b/dlls/winebth.sys/winebth.c index afcd39466e9..434604b4dad 100644 --- a/dlls/winebth.sys/winebth.c +++ b/dlls/winebth.sys/winebth.c @@ -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; diff --git a/dlls/winebth.sys/winebth_priv.h b/dlls/winebth.sys/winebth_priv.h index ca38aea44ac..cbe365b703f 100644 --- a/dlls/winebth.sys/winebth_priv.h +++ b/dlls/winebth.sys/winebth_priv.h @@ -21,6 +21,8 @@ #ifndef __WINE_WINEBTH_WINEBTH_H_ #define __WINE_WINEBTH_WINEBTH_H_ +#include +#include #include #include @@ -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 );