winegstreamer: Unblock waits in sink_chain_cb() when disabling a stream.
[wine.git] / dlls / ntoskrnl.exe / pnp.c
blob5d9ca2dca389bfc0777e4e1bf79599a268e954ff
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 static inline const char *debugstr_propkey( const DEVPROPKEY *id )
43 if (!id) return "(null)";
44 return wine_dbg_sprintf( "{%s,%04lx}", wine_dbgstr_guid( &id->fmtid ), id->pid );
47 #define MAX_SERVICE_NAME 260
49 struct device_interface
51 struct wine_rb_entry entry;
53 UNICODE_STRING symbolic_link;
54 DEVICE_OBJECT *device;
55 GUID interface_class;
56 BOOL enabled;
59 static int interface_rb_compare( const void *key, const struct wine_rb_entry *entry)
61 const struct device_interface *iface = WINE_RB_ENTRY_VALUE( entry, const struct device_interface, entry );
62 const UNICODE_STRING *k = key;
64 return RtlCompareUnicodeString( k, &iface->symbolic_link, FALSE );
67 static struct wine_rb_tree device_interfaces = { interface_rb_compare };
69 static NTSTATUS get_device_id( DEVICE_OBJECT *device, BUS_QUERY_ID_TYPE type, WCHAR **id )
71 IO_STACK_LOCATION *irpsp;
72 IO_STATUS_BLOCK irp_status;
73 KEVENT event;
74 IRP *irp;
76 device = IoGetAttachedDevice( device );
78 KeInitializeEvent( &event, NotificationEvent, FALSE );
79 if (!(irp = IoBuildSynchronousFsdRequest( IRP_MJ_PNP, device, NULL, 0, NULL, &event, &irp_status )))
80 return STATUS_NO_MEMORY;
82 irpsp = IoGetNextIrpStackLocation( irp );
83 irpsp->MinorFunction = IRP_MN_QUERY_ID;
84 irpsp->Parameters.QueryId.IdType = type;
86 irp->IoStatus.u.Status = STATUS_NOT_SUPPORTED;
87 if (IoCallDriver( device, irp ) == STATUS_PENDING)
88 KeWaitForSingleObject( &event, Executive, KernelMode, FALSE, NULL );
90 *id = (WCHAR *)irp_status.Information;
91 return irp_status.u.Status;
94 static NTSTATUS send_pnp_irp( DEVICE_OBJECT *device, UCHAR minor )
96 IO_STACK_LOCATION *irpsp;
97 IO_STATUS_BLOCK irp_status;
98 KEVENT event;
99 IRP *irp;
101 device = IoGetAttachedDevice( device );
103 KeInitializeEvent( &event, NotificationEvent, FALSE );
104 if (!(irp = IoBuildSynchronousFsdRequest( IRP_MJ_PNP, device, NULL, 0, NULL, NULL, &irp_status )))
105 return STATUS_NO_MEMORY;
107 irpsp = IoGetNextIrpStackLocation( irp );
108 irpsp->MinorFunction = minor;
110 irpsp->Parameters.StartDevice.AllocatedResources = NULL;
111 irpsp->Parameters.StartDevice.AllocatedResourcesTranslated = NULL;
113 irp->IoStatus.u.Status = STATUS_NOT_SUPPORTED;
114 if (IoCallDriver( device, irp ) == STATUS_PENDING)
115 KeWaitForSingleObject( &event, Executive, KernelMode, FALSE, NULL );
117 return irp_status.u.Status;
120 static NTSTATUS get_device_instance_id( DEVICE_OBJECT *device, WCHAR *buffer )
122 static const WCHAR backslashW[] = {'\\',0};
123 NTSTATUS status;
124 WCHAR *id;
126 if ((status = get_device_id( device, BusQueryDeviceID, &id )))
128 ERR("Failed to get device ID, status %#lx.\n", status);
129 return status;
132 lstrcpyW( buffer, id );
133 ExFreePool( id );
135 if ((status = get_device_id( device, BusQueryInstanceID, &id )))
137 ERR("Failed to get instance ID, status %#lx.\n", status);
138 return status;
141 lstrcatW( buffer, backslashW );
142 lstrcatW( buffer, id );
143 ExFreePool( id );
145 TRACE("Returning ID %s.\n", debugstr_w(buffer));
147 return STATUS_SUCCESS;
150 static NTSTATUS get_device_caps( DEVICE_OBJECT *device, DEVICE_CAPABILITIES *caps )
152 IO_STACK_LOCATION *irpsp;
153 IO_STATUS_BLOCK irp_status;
154 KEVENT event;
155 IRP *irp;
157 memset( caps, 0, sizeof(*caps) );
158 caps->Size = sizeof(*caps);
159 caps->Version = 1;
160 caps->Address = 0xffffffff;
161 caps->UINumber = 0xffffffff;
163 device = IoGetAttachedDevice( device );
165 KeInitializeEvent( &event, NotificationEvent, FALSE );
166 if (!(irp = IoBuildSynchronousFsdRequest( IRP_MJ_PNP, device, NULL, 0, NULL, NULL, &irp_status )))
167 return STATUS_NO_MEMORY;
169 irpsp = IoGetNextIrpStackLocation( irp );
170 irpsp->MinorFunction = IRP_MN_QUERY_CAPABILITIES;
171 irpsp->Parameters.DeviceCapabilities.Capabilities = caps;
173 irp->IoStatus.u.Status = STATUS_NOT_SUPPORTED;
174 if (IoCallDriver( device, irp ) == STATUS_PENDING)
175 KeWaitForSingleObject( &event, Executive, KernelMode, FALSE, NULL );
177 return irp_status.u.Status;
180 static void load_function_driver( DEVICE_OBJECT *device, HDEVINFO set, SP_DEVINFO_DATA *sp_device )
182 static const WCHAR driverW[] = {'\\','D','r','i','v','e','r','\\',0};
183 WCHAR buffer[MAX_SERVICE_NAME + ARRAY_SIZE(servicesW)];
184 WCHAR driver[MAX_SERVICE_NAME] = {0};
185 DRIVER_OBJECT *driver_obj;
186 UNICODE_STRING string;
187 NTSTATUS status;
189 if (!SetupDiGetDeviceRegistryPropertyW( set, sp_device, SPDRP_SERVICE,
190 NULL, (BYTE *)driver, sizeof(driver), NULL ))
192 WARN("No driver registered for device %p.\n", device);
193 return;
196 lstrcpyW( buffer, servicesW );
197 lstrcatW( buffer, driver );
198 RtlInitUnicodeString( &string, buffer );
199 status = ZwLoadDriver( &string );
200 if (status != STATUS_SUCCESS && status != STATUS_IMAGE_ALREADY_LOADED)
202 ERR("Failed to load driver %s, status %#lx.\n", debugstr_w(driver), status);
203 return;
206 lstrcpyW( buffer, driverW );
207 lstrcatW( buffer, driver );
208 RtlInitUnicodeString( &string, buffer );
209 if (ObReferenceObjectByName( &string, OBJ_CASE_INSENSITIVE, NULL,
210 0, NULL, KernelMode, NULL, (void **)&driver_obj ) != STATUS_SUCCESS)
212 ERR("Failed to locate loaded driver %s.\n", debugstr_w(driver));
213 return;
216 TRACE("Calling AddDevice routine %p.\n", driver_obj->DriverExtension->AddDevice);
217 if (driver_obj->DriverExtension->AddDevice)
218 status = driver_obj->DriverExtension->AddDevice( driver_obj, device );
219 else
220 status = STATUS_NOT_IMPLEMENTED;
221 TRACE("AddDevice routine %p returned %#lx.\n", driver_obj->DriverExtension->AddDevice, status);
223 ObDereferenceObject( driver_obj );
225 if (status != STATUS_SUCCESS)
226 ERR("AddDevice failed for driver %s, status %#lx.\n", debugstr_w(driver), status);
229 /* Return the total number of characters in a REG_MULTI_SZ string, including
230 * the final terminating null. */
231 static size_t sizeof_multiszW( const WCHAR *str )
233 const WCHAR *p;
234 for (p = str; *p; p += lstrlenW(p) + 1);
235 return p + 1 - str;
238 /* This does almost the same thing as UpdateDriverForPlugAndPlayDevices(),
239 * except that we don't know the INF path beforehand. */
240 static BOOL install_device_driver( DEVICE_OBJECT *device, HDEVINFO set, SP_DEVINFO_DATA *sp_device )
242 static const DWORD dif_list[] =
244 DIF_REGISTERDEVICE,
245 DIF_SELECTBESTCOMPATDRV,
246 DIF_ALLOW_INSTALL,
247 DIF_INSTALLDEVICEFILES,
248 DIF_REGISTER_COINSTALLERS,
249 DIF_INSTALLINTERFACES,
250 DIF_INSTALLDEVICE,
251 DIF_NEWDEVICEWIZARD_FINISHINSTALL,
254 NTSTATUS status;
255 unsigned int i;
256 WCHAR *ids;
258 if ((status = get_device_id( device, BusQueryHardwareIDs, &ids )) || !ids)
260 ERR("Failed to get hardware IDs, status %#lx.\n", status);
261 return FALSE;
264 SetupDiSetDeviceRegistryPropertyW( set, sp_device, SPDRP_HARDWAREID, (BYTE *)ids,
265 sizeof_multiszW( ids ) * sizeof(WCHAR) );
266 ExFreePool( ids );
268 if ((status = get_device_id( device, BusQueryCompatibleIDs, &ids )) || !ids)
270 ERR("Failed to get compatible IDs, status %#lx.\n", status);
271 return FALSE;
274 SetupDiSetDeviceRegistryPropertyW( set, sp_device, SPDRP_COMPATIBLEIDS, (BYTE *)ids,
275 sizeof_multiszW( ids ) * sizeof(WCHAR) );
276 ExFreePool( ids );
278 if (!SetupDiBuildDriverInfoList( set, sp_device, SPDIT_COMPATDRIVER ))
280 ERR("Failed to build compatible driver list, error %#lx.\n", GetLastError());
281 return FALSE;
284 for (i = 0; i < ARRAY_SIZE(dif_list); ++i)
286 if (!SetupDiCallClassInstaller(dif_list[i], set, sp_device) && GetLastError() != ERROR_DI_DO_DEFAULT)
288 WARN("Install function %#lx failed, error %#lx.\n", dif_list[i], GetLastError());
289 return FALSE;
293 return TRUE;
296 /* Load the function driver for a newly created PDO, if one is present, and
297 * send IRPs to start the device. */
298 static void start_device( DEVICE_OBJECT *device, HDEVINFO set, SP_DEVINFO_DATA *sp_device )
300 load_function_driver( device, set, sp_device );
301 if (device->DriverObject)
302 send_pnp_irp( device, IRP_MN_START_DEVICE );
305 static void enumerate_new_device( DEVICE_OBJECT *device, HDEVINFO set )
307 static const WCHAR infpathW[] = {'I','n','f','P','a','t','h',0};
309 SP_DEVINFO_DATA sp_device = {sizeof(sp_device)};
310 WCHAR device_instance_id[MAX_DEVICE_ID_LEN];
311 DEVICE_CAPABILITIES caps;
312 BOOL need_driver = TRUE;
313 NTSTATUS status;
314 HKEY key;
316 if (get_device_instance_id( device, device_instance_id ))
317 return;
319 if (!SetupDiCreateDeviceInfoW( set, device_instance_id, &GUID_NULL, NULL, NULL, 0, &sp_device )
320 && !SetupDiOpenDeviceInfoW( set, device_instance_id, NULL, 0, &sp_device ))
322 ERR("Failed to create or open device %s, error %#lx.\n", debugstr_w(device_instance_id), GetLastError());
323 return;
326 TRACE("Creating new device %s.\n", debugstr_w(device_instance_id));
328 /* Check if the device already has a driver registered; if not, find one
329 * and install it. */
330 key = SetupDiOpenDevRegKey( set, &sp_device, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_READ );
331 if (key != INVALID_HANDLE_VALUE)
333 if (!RegQueryValueExW( key, infpathW, NULL, NULL, NULL, NULL ))
334 need_driver = FALSE;
335 RegCloseKey( key );
338 if ((status = get_device_caps( device, &caps )))
340 ERR("Failed to get caps for device %s, status %#lx.\n", debugstr_w(device_instance_id), status);
341 return;
344 if (need_driver && !install_device_driver( device, set, &sp_device ) && !caps.RawDeviceOK)
346 ERR("Unable to install a function driver for device %s.\n", debugstr_w(device_instance_id));
347 return;
350 start_device( device, set, &sp_device );
353 static void send_remove_device_irp( DEVICE_OBJECT *device, UCHAR code )
355 struct wine_device *wine_device = CONTAINING_RECORD(device, struct wine_device, device_obj);
357 TRACE( "Removing device %p, code %x.\n", device, code );
359 if (wine_device->children)
361 ULONG i;
362 for (i = 0; i < wine_device->children->Count; ++i)
363 send_remove_device_irp( wine_device->children->Objects[i], code );
366 send_pnp_irp( device, code );
369 static void remove_device( DEVICE_OBJECT *device )
371 send_remove_device_irp( device, IRP_MN_SURPRISE_REMOVAL );
372 send_remove_device_irp( device, IRP_MN_REMOVE_DEVICE );
375 static BOOL device_in_list( const DEVICE_RELATIONS *list, const DEVICE_OBJECT *device )
377 ULONG i;
378 for (i = 0; i < list->Count; ++i)
380 if (list->Objects[i] == device)
381 return TRUE;
383 return FALSE;
386 static void handle_bus_relations( DEVICE_OBJECT *parent )
388 struct wine_device *wine_parent = CONTAINING_RECORD(parent, struct wine_device, device_obj);
389 SP_DEVINFO_DATA sp_device = {sizeof(sp_device)};
390 DEVICE_RELATIONS *relations;
391 IO_STATUS_BLOCK irp_status;
392 IO_STACK_LOCATION *irpsp;
393 HDEVINFO set;
394 KEVENT event;
395 IRP *irp;
396 ULONG i;
398 TRACE( "(%p)\n", parent );
400 set = SetupDiCreateDeviceInfoList( NULL, NULL );
402 parent = IoGetAttachedDevice( parent );
404 KeInitializeEvent( &event, NotificationEvent, FALSE );
405 if (!(irp = IoBuildSynchronousFsdRequest( IRP_MJ_PNP, parent, NULL, 0, NULL, &event, &irp_status )))
407 SetupDiDestroyDeviceInfoList( set );
408 return;
411 irpsp = IoGetNextIrpStackLocation( irp );
412 irpsp->MinorFunction = IRP_MN_QUERY_DEVICE_RELATIONS;
413 irpsp->Parameters.QueryDeviceRelations.Type = BusRelations;
415 irp->IoStatus.u.Status = STATUS_NOT_SUPPORTED;
416 if (IoCallDriver( parent, irp ) == STATUS_PENDING)
417 KeWaitForSingleObject( &event, Executive, KernelMode, FALSE, NULL );
419 relations = (DEVICE_RELATIONS *)irp_status.Information;
420 if (irp_status.u.Status)
422 ERR("Failed to enumerate child devices, status %#lx.\n", irp_status.u.Status);
423 SetupDiDestroyDeviceInfoList( set );
424 return;
427 TRACE("Got %lu devices.\n", relations->Count);
429 for (i = 0; i < relations->Count; ++i)
431 DEVICE_OBJECT *child = relations->Objects[i];
433 if (!wine_parent->children || !device_in_list( wine_parent->children, child ))
435 TRACE("Adding new device %p.\n", child);
436 enumerate_new_device( child, set );
440 if (wine_parent->children)
442 for (i = 0; i < wine_parent->children->Count; ++i)
444 DEVICE_OBJECT *child = wine_parent->children->Objects[i];
446 if (!device_in_list( relations, child ))
448 TRACE("Removing device %p.\n", child);
449 remove_device( child );
451 ObDereferenceObject( child );
455 ExFreePool( wine_parent->children );
456 wine_parent->children = relations;
458 SetupDiDestroyDeviceInfoList( set );
461 /***********************************************************************
462 * IoInvalidateDeviceRelations (NTOSKRNL.EXE.@)
464 void WINAPI IoInvalidateDeviceRelations( DEVICE_OBJECT *device_object, DEVICE_RELATION_TYPE type )
466 TRACE("device %p, type %#x.\n", device_object, type);
468 switch (type)
470 case BusRelations:
471 handle_bus_relations( device_object );
472 break;
473 default:
474 FIXME("Unhandled relation %#x.\n", type);
475 break;
479 /***********************************************************************
480 * IoGetDeviceProperty (NTOSKRNL.EXE.@)
482 NTSTATUS WINAPI IoGetDeviceProperty( DEVICE_OBJECT *device, DEVICE_REGISTRY_PROPERTY property,
483 ULONG length, void *buffer, ULONG *needed )
485 SP_DEVINFO_DATA sp_device = {sizeof(sp_device)};
486 WCHAR device_instance_id[MAX_DEVICE_ID_LEN];
487 DWORD sp_property = -1;
488 NTSTATUS status;
489 HDEVINFO set;
491 TRACE("device %p, property %u, length %lu, buffer %p, needed %p.\n",
492 device, property, length, buffer, needed);
494 switch (property)
496 case DevicePropertyEnumeratorName:
498 WCHAR *id, *ptr;
500 status = get_device_id( device, BusQueryInstanceID, &id );
501 if (status != STATUS_SUCCESS)
503 ERR("Failed to get instance ID, status %#lx.\n", status);
504 break;
507 wcsupr( id );
508 ptr = wcschr( id, '\\' );
509 if (ptr) *ptr = 0;
511 *needed = sizeof(WCHAR) * (lstrlenW(id) + 1);
512 if (length >= *needed)
513 memcpy( buffer, id, *needed );
514 else
515 status = STATUS_BUFFER_TOO_SMALL;
517 ExFreePool( id );
518 return status;
520 case DevicePropertyPhysicalDeviceObjectName:
522 ULONG used_len, len = length + sizeof(OBJECT_NAME_INFORMATION);
523 OBJECT_NAME_INFORMATION *name = HeapAlloc(GetProcessHeap(), 0, len);
524 HANDLE handle;
526 status = ObOpenObjectByPointer( device, OBJ_KERNEL_HANDLE, NULL, 0, NULL, KernelMode, &handle );
527 if (!status)
529 status = NtQueryObject( handle, ObjectNameInformation, name, len, &used_len );
530 NtClose( handle );
532 if (status == STATUS_SUCCESS)
534 /* Ensure room for NULL termination */
535 if (length >= name->Name.MaximumLength)
536 memcpy(buffer, name->Name.Buffer, name->Name.MaximumLength);
537 else
538 status = STATUS_BUFFER_TOO_SMALL;
539 *needed = name->Name.MaximumLength;
541 else
543 if (status == STATUS_INFO_LENGTH_MISMATCH ||
544 status == STATUS_BUFFER_OVERFLOW)
546 status = STATUS_BUFFER_TOO_SMALL;
547 *needed = used_len - sizeof(OBJECT_NAME_INFORMATION);
549 else
550 *needed = 0;
552 HeapFree(GetProcessHeap(), 0, name);
553 return status;
555 case DevicePropertyDeviceDescription:
556 sp_property = SPDRP_DEVICEDESC;
557 break;
558 case DevicePropertyHardwareID:
559 sp_property = SPDRP_HARDWAREID;
560 break;
561 case DevicePropertyCompatibleIDs:
562 sp_property = SPDRP_COMPATIBLEIDS;
563 break;
564 case DevicePropertyClassName:
565 sp_property = SPDRP_CLASS;
566 break;
567 case DevicePropertyClassGuid:
568 sp_property = SPDRP_CLASSGUID;
569 break;
570 case DevicePropertyManufacturer:
571 sp_property = SPDRP_MFG;
572 break;
573 case DevicePropertyFriendlyName:
574 sp_property = SPDRP_FRIENDLYNAME;
575 break;
576 case DevicePropertyLocationInformation:
577 sp_property = SPDRP_LOCATION_INFORMATION;
578 break;
579 case DevicePropertyBusTypeGuid:
580 sp_property = SPDRP_BUSTYPEGUID;
581 break;
582 case DevicePropertyLegacyBusType:
583 sp_property = SPDRP_LEGACYBUSTYPE;
584 break;
585 case DevicePropertyBusNumber:
586 sp_property = SPDRP_BUSNUMBER;
587 break;
588 case DevicePropertyAddress:
589 sp_property = SPDRP_ADDRESS;
590 break;
591 case DevicePropertyUINumber:
592 sp_property = SPDRP_UI_NUMBER;
593 break;
594 case DevicePropertyInstallState:
595 sp_property = SPDRP_INSTALL_STATE;
596 break;
597 case DevicePropertyRemovalPolicy:
598 sp_property = SPDRP_REMOVAL_POLICY;
599 break;
600 default:
601 FIXME("Unhandled property %u.\n", property);
602 return STATUS_NOT_IMPLEMENTED;
605 if ((status = get_device_instance_id( device, device_instance_id )))
606 return status;
608 if ((set = SetupDiCreateDeviceInfoList( &GUID_NULL, NULL )) == INVALID_HANDLE_VALUE)
610 ERR("Failed to create device list, error %#lx.\n", GetLastError());
611 return GetLastError();
614 if (!SetupDiOpenDeviceInfoW( set, device_instance_id, NULL, 0, &sp_device))
616 ERR("Failed to open device, error %#lx.\n", GetLastError());
617 SetupDiDestroyDeviceInfoList( set );
618 return GetLastError();
621 if (SetupDiGetDeviceRegistryPropertyW( set, &sp_device, sp_property, NULL, buffer, length, needed ))
622 status = STATUS_SUCCESS;
623 else
624 status = GetLastError();
626 SetupDiDestroyDeviceInfoList( set );
628 return status;
631 static NTSTATUS create_device_symlink( DEVICE_OBJECT *device, UNICODE_STRING *symlink_name )
633 UNICODE_STRING device_nameU;
634 WCHAR *device_name;
635 ULONG len = 0;
636 NTSTATUS ret;
638 ret = IoGetDeviceProperty( device, DevicePropertyPhysicalDeviceObjectName, 0, NULL, &len );
639 if (ret != STATUS_BUFFER_TOO_SMALL)
640 return ret;
642 device_name = heap_alloc( len );
643 ret = IoGetDeviceProperty( device, DevicePropertyPhysicalDeviceObjectName, len, device_name, &len );
644 if (ret)
646 heap_free( device_name );
647 return ret;
650 RtlInitUnicodeString( &device_nameU, device_name );
651 ret = IoCreateSymbolicLink( symlink_name, &device_nameU );
652 heap_free( device_name );
653 return ret;
656 void __RPC_FAR * __RPC_USER MIDL_user_allocate( SIZE_T len )
658 return heap_alloc( len );
661 void __RPC_USER MIDL_user_free( void __RPC_FAR *ptr )
663 heap_free( ptr );
666 static LONG WINAPI rpc_filter( EXCEPTION_POINTERS *eptr )
668 return I_RpcExceptionFilter( eptr->ExceptionRecord->ExceptionCode );
671 static void send_devicechange( DWORD code, void *data, unsigned int size )
673 __TRY
675 plugplay_send_event( code, data, size );
677 __EXCEPT(rpc_filter)
679 WARN("Failed to send event, exception %#lx.\n", GetExceptionCode());
681 __ENDTRY
684 /***********************************************************************
685 * IoSetDeviceInterfaceState (NTOSKRNL.EXE.@)
687 NTSTATUS WINAPI IoSetDeviceInterfaceState( UNICODE_STRING *name, BOOLEAN enable )
689 static const WCHAR DeviceClassesW[] = {'\\','R','E','G','I','S','T','R','Y','\\',
690 'M','a','c','h','i','n','e','\\','S','y','s','t','e','m','\\',
691 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
692 'C','o','n','t','r','o','l','\\',
693 'D','e','v','i','c','e','C','l','a','s','s','e','s','\\',0};
694 static const WCHAR controlW[] = {'C','o','n','t','r','o','l',0};
695 static const WCHAR linkedW[] = {'L','i','n','k','e','d',0};
696 static const WCHAR slashW[] = {'\\',0};
697 static const WCHAR hashW[] = {'#',0};
699 size_t namelen = name->Length / sizeof(WCHAR);
700 DEV_BROADCAST_DEVICEINTERFACE_W *broadcast;
701 WCHAR *path, *refstr, *p, *upper_end;
702 struct device_interface *iface;
703 HANDLE iface_key, control_key;
704 OBJECT_ATTRIBUTES attr = {0};
705 struct wine_rb_entry *entry;
706 UNICODE_STRING string;
707 DWORD data = enable;
708 NTSTATUS ret;
709 ULONG len;
711 TRACE("device %s, enable %u.\n", debugstr_us(name), enable);
713 entry = wine_rb_get( &device_interfaces, name );
714 if (!entry)
715 return STATUS_OBJECT_NAME_NOT_FOUND;
717 iface = WINE_RB_ENTRY_VALUE( entry, struct device_interface, entry );
719 if (!enable && !iface->enabled)
720 return STATUS_OBJECT_NAME_NOT_FOUND;
722 if (enable && iface->enabled)
723 return STATUS_OBJECT_NAME_EXISTS;
725 for (p = name->Buffer + 4, refstr = NULL; p < name->Buffer + namelen; p++)
726 if (*p == '\\') refstr = p;
727 if (!refstr) refstr = p;
729 len = lstrlenW(DeviceClassesW) + 38 + 1 + namelen + 2 + 1;
731 if (!(path = heap_alloc( len * sizeof(WCHAR) )))
732 return STATUS_NO_MEMORY;
734 lstrcpyW( path, DeviceClassesW );
735 lstrcpynW( path + lstrlenW( path ), refstr - 38, 39 );
736 lstrcatW( path, slashW );
737 p = path + lstrlenW( path );
738 lstrcpynW( path + lstrlenW( path ), name->Buffer, (refstr - name->Buffer) + 1 );
739 p[0] = p[1] = p[3] = '#';
740 lstrcatW( path, slashW );
741 lstrcatW( path, hashW );
742 if (refstr < name->Buffer + namelen)
743 lstrcpynW( path + lstrlenW( path ), refstr, name->Buffer + namelen - refstr + 1 );
745 attr.Length = sizeof(attr);
746 attr.ObjectName = &string;
747 RtlInitUnicodeString( &string, path );
748 ret = NtOpenKey( &iface_key, KEY_CREATE_SUB_KEY, &attr );
749 heap_free(path);
750 if (ret)
751 return ret;
753 attr.RootDirectory = iface_key;
754 RtlInitUnicodeString( &string, controlW );
755 ret = NtCreateKey( &control_key, KEY_SET_VALUE, &attr, 0, NULL, REG_OPTION_VOLATILE, NULL );
756 NtClose( iface_key );
757 if (ret)
758 return ret;
760 RtlInitUnicodeString( &string, linkedW );
761 ret = NtSetValueKey( control_key, &string, 0, REG_DWORD, &data, sizeof(data) );
762 if (ret)
764 NtClose( control_key );
765 return ret;
768 if (enable)
769 ret = create_device_symlink( iface->device, name );
770 else
771 ret = IoDeleteSymbolicLink( name );
772 if (ret)
774 NtDeleteValueKey( control_key, &string );
775 NtClose( control_key );
776 return ret;
779 iface->enabled = enable;
781 len = offsetof(DEV_BROADCAST_DEVICEINTERFACE_W, dbcc_name[namelen + 1]);
783 if ((broadcast = heap_alloc( len )))
785 broadcast->dbcc_size = len;
786 broadcast->dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
787 broadcast->dbcc_reserved = 0;
788 broadcast->dbcc_classguid = iface->interface_class;
789 lstrcpynW( broadcast->dbcc_name, name->Buffer, namelen + 1 );
790 if (namelen > 1) broadcast->dbcc_name[1] = '\\';
792 upper_end = wcschr( broadcast->dbcc_name, '#' );
793 if (upper_end) upper_end = wcschr( upper_end + 1, '#' );
794 while (upper_end && upper_end-- != broadcast->dbcc_name)
795 *upper_end = towupper( *upper_end );
797 send_devicechange( enable ? DBT_DEVICEARRIVAL : DBT_DEVICEREMOVECOMPLETE, broadcast, len );
798 heap_free( broadcast );
800 return ret;
803 /***********************************************************************
804 * IoSetDevicePropertyData (NTOSKRNL.EXE.@)
806 NTSTATUS WINAPI IoSetDevicePropertyData( DEVICE_OBJECT *device, const DEVPROPKEY *property_key, LCID lcid,
807 ULONG flags, DEVPROPTYPE type, ULONG size, void *data )
809 SP_DEVINFO_DATA sp_device = {sizeof(sp_device)};
810 WCHAR device_instance_id[MAX_DEVICE_ID_LEN];
811 NTSTATUS status;
812 HDEVINFO set;
814 TRACE( "device %p, property_key %s, lcid %#lx, flags %#lx, type %#lx, size %lu, data %p.\n",
815 device, debugstr_propkey(property_key), lcid, flags, type, size, data );
817 /* flags is always treated as PLUGPLAY_PROPERTY_PERSISTENT starting with Win 8 / 2012 */
819 if (lcid != LOCALE_NEUTRAL) FIXME( "only LOCALE_NEUTRAL is supported\n" );
821 if ((status = get_device_instance_id( device, device_instance_id ))) return status;
823 if ((set = SetupDiCreateDeviceInfoList( &GUID_NULL, NULL )) == INVALID_HANDLE_VALUE)
825 ERR( "Failed to create device list, error %#lx.\n", GetLastError() );
826 return GetLastError();
829 if (!SetupDiOpenDeviceInfoW( set, device_instance_id, NULL, 0, &sp_device ))
831 ERR( "Failed to open device, error %#lx.\n", GetLastError() );
832 SetupDiDestroyDeviceInfoList( set );
833 return GetLastError();
836 if (!SetupDiSetDevicePropertyW( set, &sp_device, property_key, type, data, size, 0 ))
838 ERR( "Failed to set property, error %#lx.\n", GetLastError() );
839 SetupDiDestroyDeviceInfoList( set );
840 return GetLastError();
843 SetupDiDestroyDeviceInfoList( set );
845 return STATUS_SUCCESS;
848 /***********************************************************************
849 * IoRegisterDeviceInterface (NTOSKRNL.EXE.@)
851 NTSTATUS WINAPI IoRegisterDeviceInterface(DEVICE_OBJECT *device, const GUID *class_guid,
852 UNICODE_STRING *refstr, UNICODE_STRING *symbolic_link)
854 SP_DEVICE_INTERFACE_DATA sp_iface = {sizeof(sp_iface)};
855 SP_DEVINFO_DATA sp_device = {sizeof(sp_device)};
856 WCHAR device_instance_id[MAX_DEVICE_ID_LEN];
857 SP_DEVICE_INTERFACE_DETAIL_DATA_W *data;
858 NTSTATUS status = STATUS_SUCCESS;
859 UNICODE_STRING device_path;
860 struct device_interface *iface;
861 struct wine_rb_entry *entry;
862 DWORD required;
863 HDEVINFO set;
865 TRACE("device %p, class_guid %s, refstr %s, symbolic_link %p.\n",
866 device, debugstr_guid(class_guid), debugstr_us(refstr), symbolic_link);
868 if ((status = get_device_instance_id( device, device_instance_id )))
869 return status;
871 set = SetupDiGetClassDevsW( class_guid, NULL, NULL, DIGCF_DEVICEINTERFACE );
872 if (set == INVALID_HANDLE_VALUE) return STATUS_UNSUCCESSFUL;
874 if (!SetupDiCreateDeviceInfoW( set, device_instance_id, class_guid, NULL, NULL, 0, &sp_device )
875 && !SetupDiOpenDeviceInfoW( set, device_instance_id, NULL, 0, &sp_device ))
877 ERR("Failed to create device %s, error %#lx.\n", debugstr_w(device_instance_id), GetLastError());
878 return GetLastError();
881 if (!SetupDiCreateDeviceInterfaceW( set, &sp_device, class_guid, refstr ? refstr->Buffer : NULL, 0, &sp_iface ))
882 return STATUS_UNSUCCESSFUL;
884 required = 0;
885 SetupDiGetDeviceInterfaceDetailW( set, &sp_iface, NULL, 0, &required, NULL );
886 if (required == 0) return STATUS_UNSUCCESSFUL;
888 data = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, required );
889 data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W);
891 if (!SetupDiGetDeviceInterfaceDetailW( set, &sp_iface, data, required, NULL, NULL ))
893 HeapFree( GetProcessHeap(), 0, data );
894 return STATUS_UNSUCCESSFUL;
897 data->DevicePath[1] = '?';
898 TRACE("Returning path %s.\n", debugstr_w(data->DevicePath));
899 RtlCreateUnicodeString( &device_path, data->DevicePath);
901 entry = wine_rb_get( &device_interfaces, &device_path );
902 if (entry)
904 iface = WINE_RB_ENTRY_VALUE( entry, struct device_interface, entry );
905 if (iface->enabled)
906 ERR("Device interface %s is still enabled.\n", debugstr_us(&iface->symbolic_link));
908 else
910 iface = heap_alloc_zero( sizeof(struct device_interface) );
911 RtlCreateUnicodeString(&iface->symbolic_link, data->DevicePath);
912 if (wine_rb_put( &device_interfaces, &iface->symbolic_link, &iface->entry ))
913 ERR("Failed to insert interface %s into tree.\n", debugstr_us(&iface->symbolic_link));
916 iface->device = device;
917 iface->interface_class = *class_guid;
918 if (symbolic_link)
919 RtlCreateUnicodeString( symbolic_link, data->DevicePath);
921 HeapFree( GetProcessHeap(), 0, data );
923 RtlFreeUnicodeString( &device_path );
925 return status;
928 /***********************************************************************
929 * IoOpenDeviceRegistryKey (NTOSKRNL.EXE.@)
931 NTSTATUS WINAPI IoOpenDeviceRegistryKey( DEVICE_OBJECT *device, ULONG type, ACCESS_MASK access, HANDLE *key )
933 SP_DEVINFO_DATA sp_device = {sizeof(sp_device)};
934 WCHAR device_instance_id[MAX_DEVICE_ID_LEN];
935 NTSTATUS status;
936 HDEVINFO set;
938 TRACE("device %p, type %#lx, access %#lx, key %p.\n", device, type, access, key);
940 if ((status = get_device_instance_id( device, device_instance_id )))
942 ERR("Failed to get device instance ID, error %#lx.\n", status);
943 return status;
946 set = SetupDiCreateDeviceInfoList( &GUID_NULL, NULL );
948 SetupDiOpenDeviceInfoW( set, device_instance_id, NULL, 0, &sp_device );
950 *key = SetupDiOpenDevRegKey( set, &sp_device, DICS_FLAG_GLOBAL, 0, type, access );
951 SetupDiDestroyDeviceInfoList( set );
952 if (*key == INVALID_HANDLE_VALUE)
953 return GetLastError();
954 return STATUS_SUCCESS;
957 /***********************************************************************
958 * PoSetPowerState (NTOSKRNL.EXE.@)
960 POWER_STATE WINAPI PoSetPowerState( DEVICE_OBJECT *device, POWER_STATE_TYPE type, POWER_STATE state)
962 FIXME("device %p, type %u, state %u, stub!\n", device, type, state.DeviceState);
963 return state;
966 /*****************************************************
967 * PoStartNextPowerIrp (NTOSKRNL.EXE.@)
969 void WINAPI PoStartNextPowerIrp( IRP *irp )
971 FIXME("irp %p, stub!\n", irp);
974 /*****************************************************
975 * PoCallDriver (NTOSKRNL.EXE.@)
977 NTSTATUS WINAPI PoCallDriver( DEVICE_OBJECT *device, IRP *irp )
979 TRACE("device %p, irp %p.\n", device, irp);
980 return IoCallDriver( device, irp );
983 static DRIVER_OBJECT *pnp_manager;
985 struct root_pnp_device
987 WCHAR id[MAX_DEVICE_ID_LEN];
988 struct list entry;
989 DEVICE_OBJECT *device;
992 static struct root_pnp_device *find_root_pnp_device( struct wine_driver *driver, const WCHAR *id )
994 struct root_pnp_device *device;
996 LIST_FOR_EACH_ENTRY( device, &driver->root_pnp_devices, struct root_pnp_device, entry )
998 if (!wcsicmp( id, device->id ))
999 return device;
1002 return NULL;
1005 static NTSTATUS WINAPI pnp_manager_device_pnp( DEVICE_OBJECT *device, IRP *irp )
1007 IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation( irp );
1008 struct root_pnp_device *root_device = device->DeviceExtension;
1009 NTSTATUS status;
1011 TRACE("device %p, irp %p, minor function %#x.\n", device, irp, stack->MinorFunction);
1013 switch (stack->MinorFunction)
1015 case IRP_MN_QUERY_DEVICE_RELATIONS:
1016 /* The FDO above already handled this, so return the same status. */
1017 break;
1018 case IRP_MN_START_DEVICE:
1019 case IRP_MN_SURPRISE_REMOVAL:
1020 /* Nothing to do. */
1021 irp->IoStatus.u.Status = STATUS_SUCCESS;
1022 break;
1023 case IRP_MN_REMOVE_DEVICE:
1024 list_remove( &root_device->entry );
1025 irp->IoStatus.u.Status = STATUS_SUCCESS;
1026 break;
1027 case IRP_MN_QUERY_CAPABILITIES:
1028 irp->IoStatus.u.Status = STATUS_SUCCESS;
1029 break;
1030 case IRP_MN_QUERY_ID:
1032 BUS_QUERY_ID_TYPE type = stack->Parameters.QueryId.IdType;
1033 WCHAR *id, *p;
1035 TRACE("Received IRP_MN_QUERY_ID, type %#x.\n", type);
1037 switch (type)
1039 case BusQueryDeviceID:
1040 p = wcsrchr( root_device->id, '\\' );
1041 if ((id = ExAllocatePool( NonPagedPool, (p - root_device->id + 1) * sizeof(WCHAR) )))
1043 memcpy( id, root_device->id, (p - root_device->id) * sizeof(WCHAR) );
1044 id[p - root_device->id] = 0;
1045 irp->IoStatus.Information = (ULONG_PTR)id;
1046 irp->IoStatus.u.Status = STATUS_SUCCESS;
1048 else
1050 irp->IoStatus.Information = 0;
1051 irp->IoStatus.u.Status = STATUS_NO_MEMORY;
1053 break;
1054 case BusQueryInstanceID:
1055 p = wcsrchr( root_device->id, '\\' );
1056 if ((id = ExAllocatePool( NonPagedPool, (wcslen( p + 1 ) + 1) * sizeof(WCHAR) )))
1058 wcscpy( id, p + 1 );
1059 irp->IoStatus.Information = (ULONG_PTR)id;
1060 irp->IoStatus.u.Status = STATUS_SUCCESS;
1062 else
1064 irp->IoStatus.Information = 0;
1065 irp->IoStatus.u.Status = STATUS_NO_MEMORY;
1067 break;
1068 default:
1069 FIXME("Unhandled IRP_MN_QUERY_ID type %#x.\n", type);
1071 break;
1073 default:
1074 FIXME("Unhandled PnP request %#x.\n", stack->MinorFunction);
1077 status = irp->IoStatus.u.Status;
1078 IoCompleteRequest( irp, IO_NO_INCREMENT );
1079 return status;
1082 static NTSTATUS WINAPI pnp_manager_driver_entry( DRIVER_OBJECT *driver, UNICODE_STRING *keypath )
1084 pnp_manager = driver;
1085 driver->MajorFunction[IRP_MJ_PNP] = pnp_manager_device_pnp;
1086 return STATUS_SUCCESS;
1089 void pnp_manager_start(void)
1091 static const WCHAR driver_nameW[] = {'\\','D','r','i','v','e','r','\\','P','n','p','M','a','n','a','g','e','r',0};
1092 WCHAR endpoint[] = L"\\pipe\\wine_plugplay";
1093 WCHAR protseq[] = L"ncacn_np";
1094 UNICODE_STRING driver_nameU;
1095 RPC_WSTR binding_str;
1096 NTSTATUS status;
1097 RPC_STATUS err;
1099 RtlInitUnicodeString( &driver_nameU, driver_nameW );
1100 if ((status = IoCreateDriver( &driver_nameU, pnp_manager_driver_entry )))
1101 ERR("Failed to create PnP manager driver, status %#lx.\n", status);
1103 if ((err = RpcStringBindingComposeW( NULL, protseq, NULL, endpoint, NULL, &binding_str )))
1105 ERR("RpcStringBindingCompose() failed, error %#lx\n", err);
1106 return;
1108 err = RpcBindingFromStringBindingW( binding_str, &plugplay_binding_handle );
1109 RpcStringFreeW( &binding_str );
1110 if (err)
1111 ERR("RpcBindingFromStringBinding() failed, error %#lx\n", err);
1114 void pnp_manager_stop_driver( struct wine_driver *driver )
1116 struct root_pnp_device *device, *next;
1118 LIST_FOR_EACH_ENTRY_SAFE( device, next, &driver->root_pnp_devices, struct root_pnp_device, entry )
1119 remove_device( device->device );
1122 void pnp_manager_stop(void)
1124 IoDeleteDriver( pnp_manager );
1125 RpcBindingFree( &plugplay_binding_handle );
1128 void CDECL wine_enumerate_root_devices( const WCHAR *driver_name )
1130 static const WCHAR driverW[] = {'\\','D','r','i','v','e','r','\\',0};
1131 static const WCHAR rootW[] = {'R','O','O','T',0};
1132 WCHAR buffer[MAX_SERVICE_NAME + ARRAY_SIZE(driverW)], id[MAX_DEVICE_ID_LEN];
1133 SP_DEVINFO_DATA sp_device = {sizeof(sp_device)};
1134 struct list new_list = LIST_INIT(new_list);
1135 struct root_pnp_device *pnp_device, *next;
1136 struct wine_driver *driver;
1137 DEVICE_OBJECT *device;
1138 NTSTATUS status;
1139 unsigned int i;
1140 HDEVINFO set;
1142 TRACE("Searching for new root-enumerated devices for driver %s.\n", debugstr_w(driver_name));
1144 driver = get_driver( driver_name );
1146 set = SetupDiGetClassDevsW( NULL, rootW, NULL, DIGCF_ALLCLASSES );
1147 if (set == INVALID_HANDLE_VALUE)
1149 ERR("Failed to build device set, error %#lx.\n", GetLastError());
1150 return;
1153 for (i = 0; SetupDiEnumDeviceInfo( set, i, &sp_device ); ++i)
1155 if (!SetupDiGetDeviceRegistryPropertyW( set, &sp_device, SPDRP_SERVICE,
1156 NULL, (BYTE *)buffer, sizeof(buffer), NULL )
1157 || lstrcmpiW( buffer, driver_name ))
1159 continue;
1162 SetupDiGetDeviceInstanceIdW( set, &sp_device, id, ARRAY_SIZE(id), NULL );
1164 if ((pnp_device = find_root_pnp_device( driver, id )))
1166 TRACE("Found device %s already enumerated.\n", debugstr_w(id));
1167 list_remove( &pnp_device->entry );
1168 list_add_tail( &new_list, &pnp_device->entry );
1169 continue;
1172 TRACE("Adding new root-enumerated device %s.\n", debugstr_w(id));
1174 if ((status = IoCreateDevice( pnp_manager, sizeof(struct root_pnp_device), NULL,
1175 FILE_DEVICE_CONTROLLER, FILE_AUTOGENERATED_DEVICE_NAME, FALSE, &device )))
1177 ERR("Failed to create root-enumerated PnP device %s, status %#lx.\n", debugstr_w(id), status);
1178 continue;
1181 pnp_device = device->DeviceExtension;
1182 wcscpy( pnp_device->id, id );
1183 pnp_device->device = device;
1184 list_add_tail( &new_list, &pnp_device->entry );
1186 start_device( device, set, &sp_device );
1189 LIST_FOR_EACH_ENTRY_SAFE( pnp_device, next, &driver->root_pnp_devices, struct root_pnp_device, entry )
1191 TRACE("Removing device %s.\n", debugstr_w(pnp_device->id));
1192 remove_device( pnp_device->device );
1195 list_move_head( &driver->root_pnp_devices, &new_list );
1197 SetupDiDestroyDeviceInfoList(set);