mfplay: Add support for same-thread event callback.
[wine.git] / dlls / ntoskrnl.exe / pnp.c
blob7994a8b85b96bcfc6db135221e91387567f91beb
1 /*
2 * Plug and Play
4 * Copyright 2016 Sebastian Lackner
5 * Copyright 2016 Aric Stewart for CodeWeavers
6 * Copyright 2019 Zebediah Figura
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #define NONAMELESSUNION
25 #include "ntoskrnl_private.h"
26 #include "winreg.h"
27 #include "winuser.h"
28 #include "setupapi.h"
29 #include "cfgmgr32.h"
30 #include "dbt.h"
31 #include "wine/exception.h"
32 #include "wine/heap.h"
34 #include "plugplay.h"
36 #include "initguid.h"
37 DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0);
39 WINE_DEFAULT_DEBUG_CHANNEL(plugplay);
41 #define MAX_SERVICE_NAME 260
43 struct device_interface
45 struct wine_rb_entry entry;
47 UNICODE_STRING symbolic_link;
48 DEVICE_OBJECT *device;
49 GUID interface_class;
50 BOOL enabled;
53 static int interface_rb_compare( const void *key, const struct wine_rb_entry *entry)
55 const struct device_interface *iface = WINE_RB_ENTRY_VALUE( entry, const struct device_interface, entry );
56 const UNICODE_STRING *k = key;
58 return RtlCompareUnicodeString( k, &iface->symbolic_link, FALSE );
61 static struct wine_rb_tree device_interfaces = { interface_rb_compare };
63 static NTSTATUS get_device_id( DEVICE_OBJECT *device, BUS_QUERY_ID_TYPE type, WCHAR **id )
65 IO_STACK_LOCATION *irpsp;
66 IO_STATUS_BLOCK irp_status;
67 KEVENT event;
68 IRP *irp;
70 device = IoGetAttachedDevice( device );
72 KeInitializeEvent( &event, NotificationEvent, FALSE );
73 if (!(irp = IoBuildSynchronousFsdRequest( IRP_MJ_PNP, device, NULL, 0, NULL, &event, &irp_status )))
74 return STATUS_NO_MEMORY;
76 irpsp = IoGetNextIrpStackLocation( irp );
77 irpsp->MinorFunction = IRP_MN_QUERY_ID;
78 irpsp->Parameters.QueryId.IdType = type;
80 irp->IoStatus.u.Status = STATUS_NOT_SUPPORTED;
81 if (IoCallDriver( device, irp ) == STATUS_PENDING)
82 KeWaitForSingleObject( &event, Executive, KernelMode, FALSE, NULL );
84 *id = (WCHAR *)irp_status.Information;
85 return irp_status.u.Status;
88 static NTSTATUS send_pnp_irp( DEVICE_OBJECT *device, UCHAR minor )
90 IO_STACK_LOCATION *irpsp;
91 IO_STATUS_BLOCK irp_status;
92 KEVENT event;
93 IRP *irp;
95 device = IoGetAttachedDevice( device );
97 KeInitializeEvent( &event, NotificationEvent, FALSE );
98 if (!(irp = IoBuildSynchronousFsdRequest( IRP_MJ_PNP, device, NULL, 0, NULL, NULL, &irp_status )))
99 return STATUS_NO_MEMORY;
101 irpsp = IoGetNextIrpStackLocation( irp );
102 irpsp->MinorFunction = minor;
104 irpsp->Parameters.StartDevice.AllocatedResources = NULL;
105 irpsp->Parameters.StartDevice.AllocatedResourcesTranslated = NULL;
107 irp->IoStatus.u.Status = STATUS_NOT_SUPPORTED;
108 if (IoCallDriver( device, irp ) == STATUS_PENDING)
109 KeWaitForSingleObject( &event, Executive, KernelMode, FALSE, NULL );
111 return irp_status.u.Status;
114 static NTSTATUS get_device_instance_id( DEVICE_OBJECT *device, WCHAR *buffer )
116 static const WCHAR backslashW[] = {'\\',0};
117 NTSTATUS status;
118 WCHAR *id;
120 if ((status = get_device_id( device, BusQueryDeviceID, &id )))
122 ERR("Failed to get device ID, status %#x.\n", status);
123 return status;
126 lstrcpyW( buffer, id );
127 ExFreePool( id );
129 if ((status = get_device_id( device, BusQueryInstanceID, &id )))
131 ERR("Failed to get instance ID, status %#x.\n", status);
132 return status;
135 lstrcatW( buffer, backslashW );
136 lstrcatW( buffer, id );
137 ExFreePool( id );
139 TRACE("Returning ID %s.\n", debugstr_w(buffer));
141 return STATUS_SUCCESS;
144 static void send_power_irp( DEVICE_OBJECT *device, DEVICE_POWER_STATE power )
146 IO_STATUS_BLOCK irp_status;
147 IO_STACK_LOCATION *irpsp;
148 KEVENT event;
149 IRP *irp;
151 device = IoGetAttachedDevice( device );
153 KeInitializeEvent( &event, NotificationEvent, FALSE );
154 if (!(irp = IoBuildSynchronousFsdRequest( IRP_MJ_POWER, device, NULL, 0, NULL, &event, &irp_status )))
155 return;
157 irpsp = IoGetNextIrpStackLocation( irp );
158 irpsp->MinorFunction = IRP_MN_SET_POWER;
160 irpsp->Parameters.Power.Type = DevicePowerState;
161 irpsp->Parameters.Power.State.DeviceState = power;
163 irp->IoStatus.u.Status = STATUS_NOT_SUPPORTED;
164 if (IoCallDriver( device, irp ) == STATUS_PENDING)
165 KeWaitForSingleObject( &event, Executive, KernelMode, FALSE, NULL );
168 static void load_function_driver( DEVICE_OBJECT *device, HDEVINFO set, SP_DEVINFO_DATA *sp_device )
170 static const WCHAR driverW[] = {'\\','D','r','i','v','e','r','\\',0};
171 WCHAR buffer[MAX_SERVICE_NAME + ARRAY_SIZE(servicesW)];
172 WCHAR driver[MAX_SERVICE_NAME] = {0};
173 DRIVER_OBJECT *driver_obj;
174 UNICODE_STRING string;
175 NTSTATUS status;
177 if (!SetupDiGetDeviceRegistryPropertyW( set, sp_device, SPDRP_SERVICE,
178 NULL, (BYTE *)driver, sizeof(driver), NULL ))
180 WARN("No driver registered for device %p.\n", device);
181 return;
184 lstrcpyW( buffer, servicesW );
185 lstrcatW( buffer, driver );
186 RtlInitUnicodeString( &string, buffer );
187 status = ZwLoadDriver( &string );
188 if (status != STATUS_SUCCESS && status != STATUS_IMAGE_ALREADY_LOADED)
190 ERR("Failed to load driver %s, status %#x.\n", debugstr_w(driver), status);
191 return;
194 lstrcpyW( buffer, driverW );
195 lstrcatW( buffer, driver );
196 RtlInitUnicodeString( &string, buffer );
197 if (ObReferenceObjectByName( &string, OBJ_CASE_INSENSITIVE, NULL,
198 0, NULL, KernelMode, NULL, (void **)&driver_obj ) != STATUS_SUCCESS)
200 ERR("Failed to locate loaded driver %s.\n", debugstr_w(driver));
201 return;
204 TRACE("Calling AddDevice routine %p.\n", driver_obj->DriverExtension->AddDevice);
205 if (driver_obj->DriverExtension->AddDevice)
206 status = driver_obj->DriverExtension->AddDevice( driver_obj, device );
207 else
208 status = STATUS_NOT_IMPLEMENTED;
209 TRACE("AddDevice routine %p returned %#x.\n", driver_obj->DriverExtension->AddDevice, status);
211 ObDereferenceObject( driver_obj );
213 if (status != STATUS_SUCCESS)
214 ERR("AddDevice failed for driver %s, status %#x.\n", debugstr_w(driver), status);
217 /* Return the total number of characters in a REG_MULTI_SZ string, including
218 * the final terminating null. */
219 static size_t sizeof_multiszW( const WCHAR *str )
221 const WCHAR *p;
222 for (p = str; *p; p += lstrlenW(p) + 1);
223 return p + 1 - str;
226 /* This does almost the same thing as UpdateDriverForPlugAndPlayDevices(),
227 * except that we don't know the INF path beforehand. */
228 static BOOL install_device_driver( DEVICE_OBJECT *device, HDEVINFO set, SP_DEVINFO_DATA *sp_device )
230 static const DWORD dif_list[] =
232 DIF_REGISTERDEVICE,
233 DIF_SELECTBESTCOMPATDRV,
234 DIF_ALLOW_INSTALL,
235 DIF_INSTALLDEVICEFILES,
236 DIF_REGISTER_COINSTALLERS,
237 DIF_INSTALLINTERFACES,
238 DIF_INSTALLDEVICE,
239 DIF_NEWDEVICEWIZARD_FINISHINSTALL,
242 NTSTATUS status;
243 unsigned int i;
244 WCHAR *ids;
246 if ((status = get_device_id( device, BusQueryHardwareIDs, &ids )) || !ids)
248 ERR("Failed to get hardware IDs, status %#x.\n", status);
249 return FALSE;
252 SetupDiSetDeviceRegistryPropertyW( set, sp_device, SPDRP_HARDWAREID, (BYTE *)ids,
253 sizeof_multiszW( ids ) * sizeof(WCHAR) );
254 ExFreePool( ids );
256 if ((status = get_device_id( device, BusQueryCompatibleIDs, &ids )) || !ids)
258 ERR("Failed to get compatible IDs, status %#x.\n", status);
259 return FALSE;
262 SetupDiSetDeviceRegistryPropertyW( set, sp_device, SPDRP_COMPATIBLEIDS, (BYTE *)ids,
263 sizeof_multiszW( ids ) * sizeof(WCHAR) );
264 ExFreePool( ids );
266 if (!SetupDiBuildDriverInfoList( set, sp_device, SPDIT_COMPATDRIVER ))
268 ERR("Failed to build compatible driver list, error %#x.\n", GetLastError());
269 return FALSE;
272 for (i = 0; i < ARRAY_SIZE(dif_list); ++i)
274 if (!SetupDiCallClassInstaller(dif_list[i], set, sp_device) && GetLastError() != ERROR_DI_DO_DEFAULT)
276 WARN("Install function %#x failed, error %#x.\n", dif_list[i], GetLastError());
277 return FALSE;
281 return TRUE;
284 /* Load the function driver for a newly created PDO, if one is present, and
285 * send IRPs to start the device. */
286 static void start_device( DEVICE_OBJECT *device, HDEVINFO set, SP_DEVINFO_DATA *sp_device )
288 load_function_driver( device, set, sp_device );
289 if (device->DriverObject)
291 send_pnp_irp( device, IRP_MN_START_DEVICE );
292 send_power_irp( device, PowerDeviceD0 );
296 static void enumerate_new_device( DEVICE_OBJECT *device, HDEVINFO set )
298 static const WCHAR infpathW[] = {'I','n','f','P','a','t','h',0};
300 SP_DEVINFO_DATA sp_device = {sizeof(sp_device)};
301 WCHAR device_instance_id[MAX_DEVICE_ID_LEN];
302 BOOL need_driver = TRUE;
303 HKEY key;
305 if (get_device_instance_id( device, device_instance_id ))
306 return;
308 if (!SetupDiCreateDeviceInfoW( set, device_instance_id, &GUID_NULL, NULL, NULL, 0, &sp_device )
309 && !SetupDiOpenDeviceInfoW( set, device_instance_id, NULL, 0, &sp_device ))
311 ERR("Failed to create or open device %s, error %#x.\n", debugstr_w(device_instance_id), GetLastError());
312 return;
315 TRACE("Creating new device %s.\n", debugstr_w(device_instance_id));
317 /* Check if the device already has a driver registered; if not, find one
318 * and install it. */
319 key = SetupDiOpenDevRegKey( set, &sp_device, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_READ );
320 if (key != INVALID_HANDLE_VALUE)
322 if (!RegQueryValueExW( key, infpathW, NULL, NULL, NULL, NULL ))
323 need_driver = FALSE;
324 RegCloseKey( key );
327 if (need_driver && !install_device_driver( device, set, &sp_device ))
328 return;
330 start_device( device, set, &sp_device );
333 static void remove_device( DEVICE_OBJECT *device )
335 struct wine_device *wine_device = CONTAINING_RECORD(device, struct wine_device, device_obj);
337 TRACE("Removing device %p.\n", device);
339 if (wine_device->children)
341 ULONG i;
342 for (i = 0; i < wine_device->children->Count; ++i)
343 remove_device( wine_device->children->Objects[i] );
346 send_power_irp( device, PowerDeviceD3 );
347 send_pnp_irp( device, IRP_MN_SURPRISE_REMOVAL );
348 send_pnp_irp( device, IRP_MN_REMOVE_DEVICE );
351 static BOOL device_in_list( const DEVICE_RELATIONS *list, const DEVICE_OBJECT *device )
353 ULONG i;
354 for (i = 0; i < list->Count; ++i)
356 if (list->Objects[i] == device)
357 return TRUE;
359 return FALSE;
362 static void handle_bus_relations( DEVICE_OBJECT *parent )
364 struct wine_device *wine_parent = CONTAINING_RECORD(parent, struct wine_device, device_obj);
365 SP_DEVINFO_DATA sp_device = {sizeof(sp_device)};
366 DEVICE_RELATIONS *relations;
367 IO_STATUS_BLOCK irp_status;
368 IO_STACK_LOCATION *irpsp;
369 HDEVINFO set;
370 KEVENT event;
371 IRP *irp;
372 ULONG i;
374 TRACE( "(%p)\n", parent );
376 set = SetupDiCreateDeviceInfoList( NULL, NULL );
378 parent = IoGetAttachedDevice( parent );
380 KeInitializeEvent( &event, NotificationEvent, FALSE );
381 if (!(irp = IoBuildSynchronousFsdRequest( IRP_MJ_PNP, parent, NULL, 0, NULL, &event, &irp_status )))
383 SetupDiDestroyDeviceInfoList( set );
384 return;
387 irpsp = IoGetNextIrpStackLocation( irp );
388 irpsp->MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS;
389 irpsp->Parameters.QueryDeviceRelations.Type = BusRelations;
391 irp->IoStatus.u.Status = STATUS_NOT_SUPPORTED;
392 if (IoCallDriver( parent, irp ) == STATUS_PENDING)
393 KeWaitForSingleObject( &event, Executive, KernelMode, FALSE, NULL );
395 relations = (DEVICE_RELATIONS *)irp_status.Information;
396 if (irp_status.u.Status)
398 ERR("Failed to enumerate child devices, status %#x.\n", irp_status.u.Status);
399 SetupDiDestroyDeviceInfoList( set );
400 return;
403 TRACE("Got %u devices.\n", relations->Count);
405 for (i = 0; i < relations->Count; ++i)
407 DEVICE_OBJECT *child = relations->Objects[i];
409 if (!wine_parent->children || !device_in_list( wine_parent->children, child ))
411 TRACE("Adding new device %p.\n", child);
412 enumerate_new_device( child, set );
416 if (wine_parent->children)
418 for (i = 0; i < wine_parent->children->Count; ++i)
420 DEVICE_OBJECT *child = wine_parent->children->Objects[i];
422 if (!device_in_list( relations, child ))
424 TRACE("Removing device %p.\n", child);
425 remove_device( child );
427 ObDereferenceObject( child );
431 ExFreePool( wine_parent->children );
432 wine_parent->children = relations;
434 SetupDiDestroyDeviceInfoList( set );
437 /***********************************************************************
438 * IoInvalidateDeviceRelations (NTOSKRNL.EXE.@)
440 void WINAPI IoInvalidateDeviceRelations( DEVICE_OBJECT *device_object, DEVICE_RELATION_TYPE type )
442 TRACE("device %p, type %#x.\n", device_object, type);
444 switch (type)
446 case BusRelations:
447 handle_bus_relations( device_object );
448 break;
449 default:
450 FIXME("Unhandled relation %#x.\n", type);
451 break;
455 /***********************************************************************
456 * IoGetDeviceProperty (NTOSKRNL.EXE.@)
458 NTSTATUS WINAPI IoGetDeviceProperty( DEVICE_OBJECT *device, DEVICE_REGISTRY_PROPERTY property,
459 ULONG length, void *buffer, ULONG *needed )
461 SP_DEVINFO_DATA sp_device = {sizeof(sp_device)};
462 WCHAR device_instance_id[MAX_DEVICE_ID_LEN];
463 DWORD sp_property = -1;
464 NTSTATUS status;
465 HDEVINFO set;
467 TRACE("device %p, property %u, length %u, buffer %p, needed %p.\n",
468 device, property, length, buffer, needed);
470 switch (property)
472 case DevicePropertyEnumeratorName:
474 WCHAR *id, *ptr;
476 status = get_device_id( device, BusQueryInstanceID, &id );
477 if (status != STATUS_SUCCESS)
479 ERR("Failed to get instance ID, status %#x.\n", status);
480 break;
483 wcsupr( id );
484 ptr = wcschr( id, '\\' );
485 if (ptr) *ptr = 0;
487 *needed = sizeof(WCHAR) * (lstrlenW(id) + 1);
488 if (length >= *needed)
489 memcpy( buffer, id, *needed );
490 else
491 status = STATUS_BUFFER_TOO_SMALL;
493 ExFreePool( id );
494 return status;
496 case DevicePropertyPhysicalDeviceObjectName:
498 ULONG used_len, len = length + sizeof(OBJECT_NAME_INFORMATION);
499 OBJECT_NAME_INFORMATION *name = HeapAlloc(GetProcessHeap(), 0, len);
500 HANDLE handle;
502 status = ObOpenObjectByPointer( device, OBJ_KERNEL_HANDLE, NULL, 0, NULL, KernelMode, &handle );
503 if (!status)
505 status = NtQueryObject( handle, ObjectNameInformation, name, len, &used_len );
506 NtClose( handle );
508 if (status == STATUS_SUCCESS)
510 /* Ensure room for NULL termination */
511 if (length >= name->Name.MaximumLength)
512 memcpy(buffer, name->Name.Buffer, name->Name.MaximumLength);
513 else
514 status = STATUS_BUFFER_TOO_SMALL;
515 *needed = name->Name.MaximumLength;
517 else
519 if (status == STATUS_INFO_LENGTH_MISMATCH ||
520 status == STATUS_BUFFER_OVERFLOW)
522 status = STATUS_BUFFER_TOO_SMALL;
523 *needed = used_len - sizeof(OBJECT_NAME_INFORMATION);
525 else
526 *needed = 0;
528 HeapFree(GetProcessHeap(), 0, name);
529 return status;
531 case DevicePropertyDeviceDescription:
532 sp_property = SPDRP_DEVICEDESC;
533 break;
534 case DevicePropertyHardwareID:
535 sp_property = SPDRP_HARDWAREID;
536 break;
537 case DevicePropertyCompatibleIDs:
538 sp_property = SPDRP_COMPATIBLEIDS;
539 break;
540 case DevicePropertyClassName:
541 sp_property = SPDRP_CLASS;
542 break;
543 case DevicePropertyClassGuid:
544 sp_property = SPDRP_CLASSGUID;
545 break;
546 case DevicePropertyManufacturer:
547 sp_property = SPDRP_MFG;
548 break;
549 case DevicePropertyFriendlyName:
550 sp_property = SPDRP_FRIENDLYNAME;
551 break;
552 case DevicePropertyLocationInformation:
553 sp_property = SPDRP_LOCATION_INFORMATION;
554 break;
555 case DevicePropertyBusTypeGuid:
556 sp_property = SPDRP_BUSTYPEGUID;
557 break;
558 case DevicePropertyLegacyBusType:
559 sp_property = SPDRP_LEGACYBUSTYPE;
560 break;
561 case DevicePropertyBusNumber:
562 sp_property = SPDRP_BUSNUMBER;
563 break;
564 case DevicePropertyAddress:
565 sp_property = SPDRP_ADDRESS;
566 break;
567 case DevicePropertyUINumber:
568 sp_property = SPDRP_UI_NUMBER;
569 break;
570 case DevicePropertyInstallState:
571 sp_property = SPDRP_INSTALL_STATE;
572 break;
573 case DevicePropertyRemovalPolicy:
574 sp_property = SPDRP_REMOVAL_POLICY;
575 break;
576 default:
577 FIXME("Unhandled property %u.\n", property);
578 return STATUS_NOT_IMPLEMENTED;
581 if ((status = get_device_instance_id( device, device_instance_id )))
582 return status;
584 if ((set = SetupDiCreateDeviceInfoList( &GUID_NULL, NULL )) == INVALID_HANDLE_VALUE)
586 ERR("Failed to create device list, error %#x.\n", GetLastError());
587 return GetLastError();
590 if (!SetupDiOpenDeviceInfoW( set, device_instance_id, NULL, 0, &sp_device))
592 ERR("Failed to open device, error %#x.\n", GetLastError());
593 SetupDiDestroyDeviceInfoList( set );
594 return GetLastError();
597 if (SetupDiGetDeviceRegistryPropertyW( set, &sp_device, sp_property, NULL, buffer, length, needed ))
598 status = STATUS_SUCCESS;
599 else
600 status = GetLastError();
602 SetupDiDestroyDeviceInfoList( set );
604 return status;
607 static NTSTATUS create_device_symlink( DEVICE_OBJECT *device, UNICODE_STRING *symlink_name )
609 UNICODE_STRING device_nameU;
610 WCHAR *device_name;
611 ULONG len = 0;
612 NTSTATUS ret;
614 ret = IoGetDeviceProperty( device, DevicePropertyPhysicalDeviceObjectName, 0, NULL, &len );
615 if (ret != STATUS_BUFFER_TOO_SMALL)
616 return ret;
618 device_name = heap_alloc( len );
619 ret = IoGetDeviceProperty( device, DevicePropertyPhysicalDeviceObjectName, len, device_name, &len );
620 if (ret)
622 heap_free( device_name );
623 return ret;
626 RtlInitUnicodeString( &device_nameU, device_name );
627 ret = IoCreateSymbolicLink( symlink_name, &device_nameU );
628 heap_free( device_name );
629 return ret;
632 void __RPC_FAR * __RPC_USER MIDL_user_allocate( SIZE_T len )
634 return heap_alloc( len );
637 void __RPC_USER MIDL_user_free( void __RPC_FAR *ptr )
639 heap_free( ptr );
642 static LONG WINAPI rpc_filter( EXCEPTION_POINTERS *eptr )
644 return I_RpcExceptionFilter( eptr->ExceptionRecord->ExceptionCode );
647 static void send_devicechange( DWORD code, void *data, unsigned int size )
649 __TRY
651 plugplay_send_event( code, data, size );
653 __EXCEPT(rpc_filter)
655 WARN("Failed to send event, exception %#x.\n", GetExceptionCode());
657 __ENDTRY
660 /***********************************************************************
661 * IoSetDeviceInterfaceState (NTOSKRNL.EXE.@)
663 NTSTATUS WINAPI IoSetDeviceInterfaceState( UNICODE_STRING *name, BOOLEAN enable )
665 static const WCHAR DeviceClassesW[] = {'\\','R','E','G','I','S','T','R','Y','\\',
666 'M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
667 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
668 'C','o','n','t','r','o','l','\\',
669 'D','e','v','i','c','e','C','l','a','s','s','e','s','\\',0};
670 static const WCHAR controlW[] = {'C','o','n','t','r','o','l',0};
671 static const WCHAR linkedW[] = {'L','i','n','k','e','d',0};
672 static const WCHAR slashW[] = {'\\',0};
673 static const WCHAR hashW[] = {'#',0};
675 size_t namelen = name->Length / sizeof(WCHAR);
676 DEV_BROADCAST_DEVICEINTERFACE_W *broadcast;
677 struct device_interface *iface;
678 HANDLE iface_key, control_key;
679 OBJECT_ATTRIBUTES attr = {0};
680 struct wine_rb_entry *entry;
681 WCHAR *path, *refstr, *p;
682 UNICODE_STRING string;
683 DWORD data = enable;
684 NTSTATUS ret;
685 ULONG len;
687 TRACE("device %s, enable %u.\n", debugstr_us(name), enable);
689 entry = wine_rb_get( &device_interfaces, name );
690 if (!entry)
691 return STATUS_OBJECT_NAME_NOT_FOUND;
693 iface = WINE_RB_ENTRY_VALUE( entry, struct device_interface, entry );
695 if (!enable && !iface->enabled)
696 return STATUS_OBJECT_NAME_NOT_FOUND;
698 if (enable && iface->enabled)
699 return STATUS_OBJECT_NAME_EXISTS;
701 for (p = name->Buffer + 4, refstr = NULL; p < name->Buffer + namelen; p++)
702 if (*p == '\\') refstr = p;
703 if (!refstr) refstr = p;
705 len = lstrlenW(DeviceClassesW) + 38 + 1 + namelen + 2 + 1;
707 if (!(path = heap_alloc( len * sizeof(WCHAR) )))
708 return STATUS_NO_MEMORY;
710 lstrcpyW( path, DeviceClassesW );
711 lstrcpynW( path + lstrlenW( path ), refstr - 38, 39 );
712 lstrcatW( path, slashW );
713 p = path + lstrlenW( path );
714 lstrcpynW( path + lstrlenW( path ), name->Buffer, (refstr - name->Buffer) + 1 );
715 p[0] = p[1] = p[3] = '#';
716 lstrcatW( path, slashW );
717 lstrcatW( path, hashW );
718 if (refstr < name->Buffer + namelen)
719 lstrcpynW( path + lstrlenW( path ), refstr, name->Buffer + namelen - refstr + 1 );
721 attr.Length = sizeof(attr);
722 attr.ObjectName = &string;
723 RtlInitUnicodeString( &string, path );
724 ret = NtOpenKey( &iface_key, KEY_CREATE_SUB_KEY, &attr );
725 heap_free(path);
726 if (ret)
727 return ret;
729 attr.RootDirectory = iface_key;
730 RtlInitUnicodeString( &string, controlW );
731 ret = NtCreateKey( &control_key, KEY_SET_VALUE, &attr, 0, NULL, REG_OPTION_VOLATILE, NULL );
732 NtClose( iface_key );
733 if (ret)
734 return ret;
736 RtlInitUnicodeString( &string, linkedW );
737 ret = NtSetValueKey( control_key, &string, 0, REG_DWORD, &data, sizeof(data) );
738 if (ret)
740 NtClose( control_key );
741 return ret;
744 if (enable)
745 ret = create_device_symlink( iface->device, name );
746 else
747 ret = IoDeleteSymbolicLink( name );
748 if (ret)
750 NtDeleteValueKey( control_key, &string );
751 NtClose( control_key );
752 return ret;
755 iface->enabled = enable;
757 len = offsetof(DEV_BROADCAST_DEVICEINTERFACE_W, dbcc_name[namelen + 1]);
759 if ((broadcast = heap_alloc( len )))
761 broadcast->dbcc_size = len;
762 broadcast->dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
763 broadcast->dbcc_reserved = 0;
764 broadcast->dbcc_classguid = iface->interface_class;
765 lstrcpynW( broadcast->dbcc_name, name->Buffer, namelen + 1 );
766 if (namelen > 1) broadcast->dbcc_name[1] = '\\';
767 send_devicechange( enable ? DBT_DEVICEARRIVAL : DBT_DEVICEREMOVECOMPLETE, broadcast, len );
768 heap_free( broadcast );
770 return ret;
773 /***********************************************************************
774 * IoRegisterDeviceInterface (NTOSKRNL.EXE.@)
776 NTSTATUS WINAPI IoRegisterDeviceInterface(DEVICE_OBJECT *device, const GUID *class_guid,
777 UNICODE_STRING *refstr, UNICODE_STRING *symbolic_link)
779 SP_DEVICE_INTERFACE_DATA sp_iface = {sizeof(sp_iface)};
780 SP_DEVINFO_DATA sp_device = {sizeof(sp_device)};
781 WCHAR device_instance_id[MAX_DEVICE_ID_LEN];
782 SP_DEVICE_INTERFACE_DETAIL_DATA_W *data;
783 NTSTATUS status = STATUS_SUCCESS;
784 UNICODE_STRING device_path;
785 struct device_interface *iface;
786 struct wine_rb_entry *entry;
787 DWORD required;
788 HDEVINFO set;
790 TRACE("device %p, class_guid %s, refstr %s, symbolic_link %p.\n",
791 device, debugstr_guid(class_guid), debugstr_us(refstr), symbolic_link);
793 if ((status = get_device_instance_id( device, device_instance_id )))
794 return status;
796 set = SetupDiGetClassDevsW( class_guid, NULL, NULL, DIGCF_DEVICEINTERFACE );
797 if (set == INVALID_HANDLE_VALUE) return STATUS_UNSUCCESSFUL;
799 if (!SetupDiCreateDeviceInfoW( set, device_instance_id, class_guid, NULL, NULL, 0, &sp_device )
800 && !SetupDiOpenDeviceInfoW( set, device_instance_id, NULL, 0, &sp_device ))
802 ERR("Failed to create device %s, error %#x.\n", debugstr_w(device_instance_id), GetLastError());
803 return GetLastError();
806 if (!SetupDiCreateDeviceInterfaceW( set, &sp_device, class_guid, refstr ? refstr->Buffer : NULL, 0, &sp_iface ))
807 return STATUS_UNSUCCESSFUL;
809 required = 0;
810 SetupDiGetDeviceInterfaceDetailW( set, &sp_iface, NULL, 0, &required, NULL );
811 if (required == 0) return STATUS_UNSUCCESSFUL;
813 data = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, required );
814 data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W);
816 if (!SetupDiGetDeviceInterfaceDetailW( set, &sp_iface, data, required, NULL, NULL ))
818 HeapFree( GetProcessHeap(), 0, data );
819 return STATUS_UNSUCCESSFUL;
822 data->DevicePath[1] = '?';
823 TRACE("Returning path %s.\n", debugstr_w(data->DevicePath));
824 RtlCreateUnicodeString( &device_path, data->DevicePath);
826 entry = wine_rb_get( &device_interfaces, &device_path );
827 if (entry)
829 iface = WINE_RB_ENTRY_VALUE( entry, struct device_interface, entry );
830 if (iface->enabled)
831 ERR("Device interface %s is still enabled.\n", debugstr_us(&iface->symbolic_link));
833 else
835 iface = heap_alloc_zero( sizeof(struct device_interface) );
836 RtlCreateUnicodeString(&iface->symbolic_link, data->DevicePath);
837 if (wine_rb_put( &device_interfaces, &iface->symbolic_link, &iface->entry ))
838 ERR("Failed to insert interface %s into tree.\n", debugstr_us(&iface->symbolic_link));
841 iface->device = device;
842 iface->interface_class = *class_guid;
843 if (symbolic_link)
844 RtlCreateUnicodeString( symbolic_link, data->DevicePath);
846 HeapFree( GetProcessHeap(), 0, data );
848 RtlFreeUnicodeString( &device_path );
850 return status;
853 /***********************************************************************
854 * IoOpenDeviceRegistryKey (NTOSKRNL.EXE.@)
856 NTSTATUS WINAPI IoOpenDeviceRegistryKey( DEVICE_OBJECT *device, ULONG type, ACCESS_MASK access, HANDLE *key )
858 SP_DEVINFO_DATA sp_device = {sizeof(sp_device)};
859 WCHAR device_instance_id[MAX_DEVICE_ID_LEN];
860 NTSTATUS status;
861 HDEVINFO set;
863 TRACE("device %p, type %#x, access %#x, key %p.\n", device, type, access, key);
865 if ((status = get_device_instance_id( device, device_instance_id )))
867 ERR("Failed to get device instance ID, error %#x.\n", status);
868 return status;
871 set = SetupDiCreateDeviceInfoList( &GUID_NULL, NULL );
873 SetupDiOpenDeviceInfoW( set, device_instance_id, NULL, 0, &sp_device );
875 *key = SetupDiOpenDevRegKey( set, &sp_device, DICS_FLAG_GLOBAL, 0, type, access );
876 SetupDiDestroyDeviceInfoList( set );
877 if (*key == INVALID_HANDLE_VALUE)
878 return GetLastError();
879 return STATUS_SUCCESS;
882 /***********************************************************************
883 * PoSetPowerState (NTOSKRNL.EXE.@)
885 POWER_STATE WINAPI PoSetPowerState( DEVICE_OBJECT *device, POWER_STATE_TYPE type, POWER_STATE state)
887 FIXME("device %p, type %u, state %u, stub!\n", device, type, state.DeviceState);
888 return state;
891 /*****************************************************
892 * PoStartNextPowerIrp (NTOSKRNL.EXE.@)
894 void WINAPI PoStartNextPowerIrp( IRP *irp )
896 FIXME("irp %p, stub!\n", irp);
899 /*****************************************************
900 * PoCallDriver (NTOSKRNL.EXE.@)
902 NTSTATUS WINAPI PoCallDriver( DEVICE_OBJECT *device, IRP *irp )
904 TRACE("device %p, irp %p.\n", device, irp);
905 return IoCallDriver( device, irp );
908 static DRIVER_OBJECT *pnp_manager;
910 struct root_pnp_device
912 WCHAR id[MAX_DEVICE_ID_LEN];
913 struct list entry;
914 DEVICE_OBJECT *device;
917 static struct root_pnp_device *find_root_pnp_device( struct wine_driver *driver, const WCHAR *id )
919 struct root_pnp_device *device;
921 LIST_FOR_EACH_ENTRY( device, &driver->root_pnp_devices, struct root_pnp_device, entry )
923 if (!wcsicmp( id, device->id ))
924 return device;
927 return NULL;
930 static NTSTATUS WINAPI pnp_manager_device_pnp( DEVICE_OBJECT *device, IRP *irp )
932 IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp );
933 struct root_pnp_device *root_device = device->DeviceExtension;
934 NTSTATUS status;
936 TRACE("device %p, irp %p, minor function %#x.\n", device, irp, stack->MinorFunction);
938 switch (stack->MinorFunction)
940 case IRP_MN_QUERY_DEVICE_RELATIONS:
941 /* The FDO above already handled this, so return the same status. */
942 break;
943 case IRP_MN_START_DEVICE:
944 case IRP_MN_SURPRISE_REMOVAL:
945 case IRP_MN_REMOVE_DEVICE:
946 /* Nothing to do. */
947 irp->IoStatus.u.Status = STATUS_SUCCESS;
948 break;
949 case IRP_MN_QUERY_CAPABILITIES:
950 irp->IoStatus.u.Status = STATUS_SUCCESS;
951 break;
952 case IRP_MN_QUERY_ID:
954 BUS_QUERY_ID_TYPE type = stack->Parameters.QueryId.IdType;
955 WCHAR *id, *p;
957 TRACE("Received IRP_MN_QUERY_ID, type %#x.\n", type);
959 switch (type)
961 case BusQueryDeviceID:
962 p = wcsrchr( root_device->id, '\\' );
963 if ((id = ExAllocatePool( NonPagedPool, (p - root_device->id + 1) * sizeof(WCHAR) )))
965 memcpy( id, root_device->id, (p - root_device->id) * sizeof(WCHAR) );
966 id[p - root_device->id] = 0;
967 irp->IoStatus.Information = (ULONG_PTR)id;
968 irp->IoStatus.u.Status = STATUS_SUCCESS;
970 else
972 irp->IoStatus.Information = 0;
973 irp->IoStatus.u.Status = STATUS_NO_MEMORY;
975 break;
976 case BusQueryInstanceID:
977 p = wcsrchr( root_device->id, '\\' );
978 if ((id = ExAllocatePool( NonPagedPool, (wcslen( p + 1 ) + 1) * sizeof(WCHAR) )))
980 wcscpy( id, p + 1 );
981 irp->IoStatus.Information = (ULONG_PTR)id;
982 irp->IoStatus.u.Status = STATUS_SUCCESS;
984 else
986 irp->IoStatus.Information = 0;
987 irp->IoStatus.u.Status = STATUS_NO_MEMORY;
989 break;
990 default:
991 FIXME("Unhandled IRP_MN_QUERY_ID type %#x.\n", type);
993 break;
995 default:
996 FIXME("Unhandled PnP request %#x.\n", stack->MinorFunction);
999 status = irp->IoStatus.u.Status;
1000 IoCompleteRequest( irp, IO_NO_INCREMENT );
1001 return status;
1004 static NTSTATUS WINAPI pnp_manager_driver_entry( DRIVER_OBJECT *driver, UNICODE_STRING *keypath )
1006 pnp_manager = driver;
1007 driver->MajorFunction[IRP_MJ_PNP] = pnp_manager_device_pnp;
1008 return STATUS_SUCCESS;
1011 void pnp_manager_start(void)
1013 static const WCHAR driver_nameW[] = {'\\','D','r','i','v','e','r','\\','P','n','p','M','a','n','a','g','e','r',0};
1014 WCHAR endpoint[] = L"\\pipe\\wine_plugplay";
1015 WCHAR protseq[] = L"ncalrpc";
1016 UNICODE_STRING driver_nameU;
1017 RPC_WSTR binding_str;
1018 NTSTATUS status;
1019 RPC_STATUS err;
1021 RtlInitUnicodeString( &driver_nameU, driver_nameW );
1022 if ((status = IoCreateDriver( &driver_nameU, pnp_manager_driver_entry )))
1023 ERR("Failed to create PnP manager driver, status %#x.\n", status);
1025 if ((err = RpcStringBindingComposeW( NULL, protseq, NULL, endpoint, NULL, &binding_str )))
1027 ERR("RpcStringBindingCompose() failed, error %#x\n", err);
1028 return;
1030 err = RpcBindingFromStringBindingW( binding_str, &plugplay_binding_handle );
1031 RpcStringFreeW( &binding_str );
1032 if (err)
1033 ERR("RpcBindingFromStringBinding() failed, error %#x\n", err);
1036 void pnp_manager_stop_driver( struct wine_driver *driver )
1038 struct root_pnp_device *device, *next;
1040 LIST_FOR_EACH_ENTRY_SAFE( device, next, &driver->root_pnp_devices, struct root_pnp_device, entry )
1041 remove_device( device->device );
1044 void pnp_manager_stop(void)
1046 IoDeleteDriver( pnp_manager );
1047 RpcBindingFree( &plugplay_binding_handle );
1050 void CDECL wine_enumerate_root_devices( const WCHAR *driver_name )
1052 static const WCHAR driverW[] = {'\\','D','r','i','v','e','r','\\',0};
1053 static const WCHAR rootW[] = {'R','O','O','T',0};
1054 WCHAR buffer[MAX_SERVICE_NAME + ARRAY_SIZE(driverW)], id[MAX_DEVICE_ID_LEN];
1055 SP_DEVINFO_DATA sp_device = {sizeof(sp_device)};
1056 struct list new_list = LIST_INIT(new_list);
1057 struct root_pnp_device *pnp_device, *next;
1058 struct wine_driver *driver;
1059 DEVICE_OBJECT *device;
1060 NTSTATUS status;
1061 unsigned int i;
1062 HDEVINFO set;
1064 TRACE("Searching for new root-enumerated devices for driver %s.\n", debugstr_w(driver_name));
1066 driver = get_driver( driver_name );
1068 set = SetupDiGetClassDevsW( NULL, rootW, NULL, DIGCF_ALLCLASSES );
1069 if (set == INVALID_HANDLE_VALUE)
1071 ERR("Failed to build device set, error %#x.\n", GetLastError());
1072 return;
1075 for (i = 0; SetupDiEnumDeviceInfo( set, i, &sp_device ); ++i)
1077 if (!SetupDiGetDeviceRegistryPropertyW( set, &sp_device, SPDRP_SERVICE,
1078 NULL, (BYTE *)buffer, sizeof(buffer), NULL )
1079 || lstrcmpiW( buffer, driver_name ))
1081 continue;
1084 SetupDiGetDeviceInstanceIdW( set, &sp_device, id, ARRAY_SIZE(id), NULL );
1086 if ((pnp_device = find_root_pnp_device( driver, id )))
1088 TRACE("Found device %s already enumerated.\n", debugstr_w(id));
1089 list_remove( &pnp_device->entry );
1090 list_add_tail( &new_list, &pnp_device->entry );
1091 continue;
1094 TRACE("Adding new root-enumerated device %s.\n", debugstr_w(id));
1096 if ((status = IoCreateDevice( pnp_manager, sizeof(struct root_pnp_device), NULL,
1097 FILE_DEVICE_CONTROLLER, FILE_AUTOGENERATED_DEVICE_NAME, FALSE, &device )))
1099 ERR("Failed to create root-enumerated PnP device %s, status %#x.\n", debugstr_w(id), status);
1100 continue;
1103 pnp_device = device->DeviceExtension;
1104 wcscpy( pnp_device->id, id );
1105 pnp_device->device = device;
1106 list_add_tail( &new_list, &pnp_device->entry );
1108 start_device( device, set, &sp_device );
1111 LIST_FOR_EACH_ENTRY_SAFE( pnp_device, next, &driver->root_pnp_devices, struct root_pnp_device, entry )
1113 TRACE("Removing device %s.\n", debugstr_w(pnp_device->id));
1115 remove_device( pnp_device->device );
1118 list_move_head( &driver->root_pnp_devices, &new_list );
1120 SetupDiDestroyDeviceInfoList(set);