- Remove root device hack.
[reactos.git] / reactos / ntoskrnl / io / iomgr / device.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/io/device.c
5 * PURPOSE: Device Object Management, including Notifications and Queues.
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 * Filip Navara (navaraf@reactos.org)
8 * Hervé Poussineau (hpoussin@reactos.org)
9 */
10
11 /* INCLUDES *******************************************************************/
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <internal/debug.h>
16
17 /* GLOBALS ********************************************************************/
18
19 ULONG IopDeviceObjectNumber = 0;
20
21 LIST_ENTRY ShutdownListHead, LastChanceShutdownListHead;
22 KSPIN_LOCK ShutdownListLock;
23
24 /* PRIVATE FUNCTIONS **********************************************************/
25
26 PDEVICE_OBJECT
27 NTAPI
28 IopAttachDeviceToDeviceStackSafe(IN PDEVICE_OBJECT SourceDevice,
29 IN PDEVICE_OBJECT TargetDevice,
30 OUT PDEVICE_OBJECT *AttachedToDeviceObject OPTIONAL)
31 {
32 PDEVICE_OBJECT AttachedDevice;
33 PEXTENDED_DEVOBJ_EXTENSION SourceDeviceExtension;
34
35 /* Get the Attached Device and source extension */
36 AttachedDevice = IoGetAttachedDevice(TargetDevice);
37 SourceDeviceExtension = IoGetDevObjExtension(SourceDevice);
38 ASSERT(SourceDeviceExtension->AttachedTo == NULL);
39
40 /* Make sure that it's in a correct state */
41 if ((AttachedDevice->Flags & DO_DEVICE_INITIALIZING) ||
42 (IoGetDevObjExtension(AttachedDevice)->ExtensionFlags &
43 (DOE_UNLOAD_PENDING |
44 DOE_DELETE_PENDING |
45 DOE_REMOVE_PENDING |
46 DOE_REMOVE_PROCESSED)))
47 {
48 /* Device was unloading or being removed */
49 AttachedDevice = NULL;
50 }
51 else
52 {
53 /* Update atached device fields */
54 AttachedDevice->AttachedDevice = SourceDevice;
55 AttachedDevice->Spare1++;
56
57 /* Update the source with the attached data */
58 SourceDevice->StackSize = AttachedDevice->StackSize + 1;
59 SourceDevice->AlignmentRequirement = AttachedDevice->
60 AlignmentRequirement;
61 SourceDevice->SectorSize = AttachedDevice->SectorSize;
62
63 /* Check for pending start flag */
64 if (IoGetDevObjExtension(AttachedDevice)->ExtensionFlags &
65 DOE_START_PENDING)
66 {
67 /* Propagate */
68 IoGetDevObjExtension(SourceDevice)->ExtensionFlags |=
69 DOE_START_PENDING;
70 }
71
72 /* Set the attachment in the device extension */
73 SourceDeviceExtension->AttachedTo = AttachedDevice;
74 }
75
76 /* Return the attached device */
77 if (AttachedToDeviceObject) *AttachedToDeviceObject = AttachedDevice;
78 return AttachedDevice;
79 }
80
81 VOID
82 NTAPI
83 IoShutdownRegisteredDevices(VOID)
84 {
85 PLIST_ENTRY ListEntry;
86 PDEVICE_OBJECT DeviceObject;
87 PSHUTDOWN_ENTRY ShutdownEntry;
88 IO_STATUS_BLOCK StatusBlock;
89 PIRP Irp;
90 KEVENT Event;
91 NTSTATUS Status;
92
93 /* Initialize an event to wait on */
94 KeInitializeEvent(&Event, NotificationEvent, FALSE);
95
96 /* Get the first entry and start looping */
97 ListEntry = ExInterlockedRemoveHeadList(&ShutdownListHead,
98 &ShutdownListLock);
99 while (ListEntry)
100 {
101 /* Get the shutdown entry */
102 ShutdownEntry = CONTAINING_RECORD(ListEntry,
103 SHUTDOWN_ENTRY,
104 ShutdownList);
105
106 /* Get the attached device */
107 DeviceObject = IoGetAttachedDevice(ShutdownEntry->DeviceObject);
108
109 /* Build the shutdown IRP and call the driver */
110 Irp = IoBuildSynchronousFsdRequest(IRP_MJ_SHUTDOWN,
111 DeviceObject,
112 NULL,
113 0,
114 NULL,
115 &Event,
116 &StatusBlock);
117 Status = IoCallDriver(DeviceObject, Irp);
118 if (Status == STATUS_PENDING)
119 {
120 /* Wait on the driver */
121 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
122 }
123
124 /* Free the shutdown entry and reset the event */
125 ExFreePool(ShutdownEntry);
126 KeClearEvent(&Event);
127
128 /* Go to the next entry */
129 ListEntry = ExInterlockedRemoveHeadList(&ShutdownListHead,
130 &ShutdownListLock);
131 }
132 }
133
134 NTSTATUS
135 NTAPI
136 IopGetDeviceObjectPointer(IN PUNICODE_STRING ObjectName,
137 IN ACCESS_MASK DesiredAccess,
138 OUT PFILE_OBJECT *FileObject,
139 OUT PDEVICE_OBJECT *DeviceObject,
140 IN ULONG AttachFlag)
141 {
142 OBJECT_ATTRIBUTES ObjectAttributes;
143 IO_STATUS_BLOCK StatusBlock;
144 PFILE_OBJECT LocalFileObject;
145 HANDLE FileHandle;
146 NTSTATUS Status;
147
148 /* Open the Device */
149 InitializeObjectAttributes(&ObjectAttributes,
150 ObjectName,
151 OBJ_KERNEL_HANDLE,
152 NULL,
153 NULL);
154 Status = ZwOpenFile(&FileHandle,
155 DesiredAccess,
156 &ObjectAttributes,
157 &StatusBlock,
158 0,
159 FILE_NON_DIRECTORY_FILE | AttachFlag);
160 if (!NT_SUCCESS(Status)) return Status;
161
162 /* Get File Object */
163 Status = ObReferenceObjectByHandle(FileHandle,
164 0,
165 IoFileObjectType,
166 KernelMode,
167 (PVOID*)&LocalFileObject,
168 NULL);
169 if (NT_SUCCESS(Status))
170 {
171 /* Return the requested data */
172 *DeviceObject = IoGetRelatedDeviceObject(LocalFileObject);
173 *FileObject = LocalFileObject;
174 ZwClose(FileHandle);
175 }
176
177 /* Close the handle */
178 return Status;
179 }
180
181 PDEVICE_OBJECT
182 NTAPI
183 IopGetLowestDevice(IN PDEVICE_OBJECT DeviceObject)
184 {
185 PDEVICE_OBJECT LowestDevice;
186 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
187
188 /* Get the current device and its extension */
189 LowestDevice = DeviceObject;
190 DeviceExtension = IoGetDevObjExtension(LowestDevice);
191
192 /* Keep looping as long as we're attached */
193 while (DeviceExtension->AttachedTo)
194 {
195 /* Get the lowest device and its extension */
196 LowestDevice = DeviceExtension->AttachedTo;
197 DeviceExtension = IoGetDevObjExtension(LowestDevice);
198 }
199
200 /* Return the lowest device */
201 return LowestDevice;
202 }
203
204 VOID
205 NTAPI
206 IopEditDeviceList(IN PDRIVER_OBJECT DriverObject,
207 IN PDEVICE_OBJECT DeviceObject,
208 IN IOP_DEVICE_LIST_OPERATION Type)
209 {
210 PDEVICE_OBJECT Previous;
211
212 /* Check the type of operation */
213 if (Type == IopRemove)
214 {
215 /* Get the current device and check if it's the current one */
216 Previous = DeviceObject->DriverObject->DeviceObject;
217 if (Previous == DeviceObject)
218 {
219 /* It is, simply unlink this one directly */
220 DeviceObject->DriverObject->DeviceObject =
221 DeviceObject->NextDevice;
222 }
223 else
224 {
225 /* It's not, so loop until we find the device */
226 while (Previous->NextDevice != DeviceObject)
227 {
228 /* Not this one, keep moving */
229 Previous = Previous->NextDevice;
230 }
231
232 /* We found it, now unlink us */
233 Previous->NextDevice = DeviceObject->NextDevice;
234 }
235 }
236 else
237 {
238 /* Link the device object and the driver object */
239 DeviceObject->NextDevice = DriverObject->DeviceObject;
240 DriverObject->DeviceObject = DeviceObject;
241 }
242 }
243
244 VOID
245 NTAPI
246 IopUnloadDevice(IN PDEVICE_OBJECT DeviceObject)
247 {
248 PDRIVER_OBJECT DriverObject = DeviceObject->DriverObject;
249 PDEVICE_OBJECT AttachedDeviceObject, LowestDeviceObject;
250 PEXTENDED_DEVOBJ_EXTENSION ThisExtension, DeviceExtension;
251 PDEVICE_NODE DeviceNode;
252 BOOLEAN SafeToUnload = TRUE;
253
254 /* Check if removal is pending */
255 ThisExtension = IoGetDevObjExtension(DeviceObject);
256 if (ThisExtension->ExtensionFlags & DOE_REMOVE_PENDING)
257 {
258 /* Get the PDO, extension, and node */
259 LowestDeviceObject = IopGetLowestDevice(DeviceObject);
260 DeviceExtension = IoGetDevObjExtension(LowestDeviceObject);
261 DeviceNode = DeviceExtension->DeviceNode;
262
263 /* The PDO needs a device node */
264 ASSERT(DeviceNode != NULL);
265
266 /* Loop all attached objects */
267 AttachedDeviceObject = LowestDeviceObject;
268 while (AttachedDeviceObject)
269 {
270 /* Make sure they're dereferenced */
271 if (AttachedDeviceObject->ReferenceCount) return;
272 AttachedDeviceObject = AttachedDeviceObject->AttachedDevice;
273 }
274
275 /* Loop all attached objects */
276 AttachedDeviceObject = LowestDeviceObject;
277 while (AttachedDeviceObject)
278 {
279 /* Get the device extension */
280 DeviceExtension = IoGetDevObjExtension(AttachedDeviceObject);
281
282 /* Remove the pending flag and set processed */
283 DeviceExtension->ExtensionFlags &= ~DOE_REMOVE_PENDING;
284 DeviceExtension->ExtensionFlags |= DOE_REMOVE_PROCESSED;
285 AttachedDeviceObject = AttachedDeviceObject->AttachedDevice;
286 }
287
288 /*
289 * FIXME: TODO HPOUSSIN
290 * We need to parse/lock the device node, and if we have any pending
291 * surprise removals, query all relationships and send IRP_MN_REMOVE_
292 * _DEVICE to the devices related...
293 */
294 return;
295 }
296
297 /* Check if deletion is pending */
298 if (ThisExtension->ExtensionFlags & DOE_DELETE_PENDING)
299 {
300 /* Make sure unload is pending */
301 if (!(ThisExtension->ExtensionFlags & DOE_UNLOAD_PENDING) ||
302 (DriverObject->Flags & DRVO_UNLOAD_INVOKED))
303 {
304 /* We can't unload anymore */
305 SafeToUnload = FALSE;
306 }
307
308 /*
309 * Check if we have an attached device and fail if we're attached
310 * and still have a reference count.
311 */
312 AttachedDeviceObject = DeviceObject->AttachedDevice;
313 if ((AttachedDeviceObject) && (DeviceObject->ReferenceCount)) return;
314
315 /* Check if we have a Security Descriptor */
316 if (DeviceObject->SecurityDescriptor)
317 {
318 /* Free it */
319 ExFreePool(DeviceObject->SecurityDescriptor);
320 }
321
322 /* Remove the device from the list */
323 IopEditDeviceList(DeviceObject->DriverObject, DeviceObject, IopRemove);
324
325 /* Dereference the keep-alive */
326 ObDereferenceObject(DeviceObject);
327
328 /* If we're not unloading, stop here */
329 if (!SafeToUnload) return;
330 }
331
332 /* Loop all the device objects */
333 DeviceObject = DriverObject->DeviceObject;
334 while (DeviceObject)
335 {
336 /*
337 * Make sure we're not attached, having a reference count
338 * or already deleting
339 */
340 if ((DeviceObject->ReferenceCount) ||
341 (DeviceObject->AttachedDevice) ||
342 (IoGetDevObjExtension(DeviceObject)->ExtensionFlags &
343 (DOE_DELETE_PENDING | DOE_REMOVE_PENDING)))
344 {
345 /* We're not safe to unload, quit */
346 return;
347 }
348
349 /* Check the next device */
350 DeviceObject = DeviceObject->NextDevice;
351 }
352
353 /* Set the unload invoked flag */
354 DriverObject->Flags |= DRVO_UNLOAD_INVOKED;
355
356 /* Unload it */
357 if (DriverObject->DriverUnload) DriverObject->DriverUnload(DriverObject);
358 }
359
360 VOID
361 NTAPI
362 IopDereferenceDeviceObject(IN PDEVICE_OBJECT DeviceObject,
363 IN BOOLEAN ForceUnload)
364 {
365 /* Sanity check */
366 ASSERT(DeviceObject->ReferenceCount);
367
368 /* Dereference the device */
369 DeviceObject->ReferenceCount--;
370
371 /*
372 * Check if we can unload it and it's safe to unload (or if we're forcing
373 * an unload, which is OK too).
374 */
375 if (!(DeviceObject->ReferenceCount) &&
376 ((ForceUnload) || (IoGetDevObjExtension(DeviceObject)->ExtensionFlags &
377 (DOE_UNLOAD_PENDING |
378 DOE_DELETE_PENDING |
379 DOE_REMOVE_PENDING |
380 DOE_REMOVE_PROCESSED))))
381 {
382 /* Unload it */
383 IopUnloadDevice(DeviceObject);
384 }
385 }
386
387 VOID
388 NTAPI
389 IopStartNextPacketByKey(IN PDEVICE_OBJECT DeviceObject,
390 IN BOOLEAN Cancelable,
391 IN ULONG Key)
392 {
393 PKDEVICE_QUEUE_ENTRY Entry;
394 PIRP Irp;
395 KIRQL OldIrql;
396
397 /* Acquire the cancel lock if this is cancelable */
398 if (Cancelable) IoAcquireCancelSpinLock(&OldIrql);
399
400 /* Clear the current IRP */
401 DeviceObject->CurrentIrp = NULL;
402
403 /* Remove an entry from the queue */
404 Entry = KeRemoveByKeyDeviceQueue(&DeviceObject->DeviceQueue, Key);
405 if (Entry)
406 {
407 /* Get the IRP and set it */
408 Irp = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.DeviceQueueEntry);
409 DeviceObject->CurrentIrp = Irp;
410
411 /* Check if this is a cancelable packet */
412 if (Cancelable)
413 {
414 /* Check if the caller requested no cancellation */
415 if (IoGetDevObjExtension(DeviceObject)->StartIoFlags &
416 DOE_SIO_NO_CANCEL)
417 {
418 /* He did, so remove the cancel routine */
419 Irp->CancelRoutine = NULL;
420 }
421
422 /* Release the cancel lock */
423 IoReleaseCancelSpinLock(OldIrql);
424 }
425
426 /* Call the Start I/O Routine */
427 DeviceObject->DriverObject->DriverStartIo(DeviceObject, Irp);
428 }
429 else
430 {
431 /* Otherwise, release the cancel lock if we had acquired it */
432 if (Cancelable) IoReleaseCancelSpinLock(OldIrql);
433 }
434 }
435
436 VOID
437 NTAPI
438 IopStartNextPacket(IN PDEVICE_OBJECT DeviceObject,
439 IN BOOLEAN Cancelable)
440 {
441 PKDEVICE_QUEUE_ENTRY Entry;
442 PIRP Irp;
443 KIRQL OldIrql;
444
445 /* Acquire the cancel lock if this is cancelable */
446 if (Cancelable) IoAcquireCancelSpinLock(&OldIrql);
447
448 /* Clear the current IRP */
449 DeviceObject->CurrentIrp = NULL;
450
451 /* Remove an entry from the queue */
452 Entry = KeRemoveDeviceQueue(&DeviceObject->DeviceQueue);
453 if (Entry)
454 {
455 /* Get the IRP and set it */
456 Irp = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.DeviceQueueEntry);
457 DeviceObject->CurrentIrp = Irp;
458
459 /* Check if this is a cancelable packet */
460 if (Cancelable)
461 {
462 /* Check if the caller requested no cancellation */
463 if (IoGetDevObjExtension(DeviceObject)->StartIoFlags &
464 DOE_SIO_NO_CANCEL)
465 {
466 /* He did, so remove the cancel routine */
467 Irp->CancelRoutine = NULL;
468 }
469
470 /* Release the cancel lock */
471 IoReleaseCancelSpinLock(OldIrql);
472 }
473
474 /* Call the Start I/O Routine */
475 DeviceObject->DriverObject->DriverStartIo(DeviceObject, Irp);
476 }
477 else
478 {
479 /* Otherwise, release the cancel lock if we had acquired it */
480 if (Cancelable) IoReleaseCancelSpinLock(OldIrql);
481 }
482 }
483
484 VOID
485 NTAPI
486 IopStartNextPacketByKeyEx(IN PDEVICE_OBJECT DeviceObject,
487 IN ULONG Key,
488 IN ULONG Flags)
489 {
490 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
491 ULONG CurrentKey = Key;
492 ULONG CurrentFlags = Flags;
493
494 /* Get the device extension and start the packet loop */
495 DeviceExtension = IoGetDevObjExtension(DeviceObject);
496 while (TRUE)
497 {
498 /* Increase the count */
499 if (InterlockedIncrement(&DeviceExtension->StartIoCount) > 1)
500 {
501 /*
502 * We've already called the routine once...
503 * All we have to do is save the key and add the new flags
504 */
505 DeviceExtension->StartIoFlags |= CurrentFlags;
506 DeviceExtension->StartIoKey = CurrentKey;
507 }
508 else
509 {
510 /* Mask out the current packet flags and key */
511 DeviceExtension->StartIoFlags &= ~(DOE_SIO_WITH_KEY |
512 DOE_SIO_NO_KEY |
513 DOE_SIO_CANCELABLE);
514 DeviceExtension->StartIoKey = 0;
515
516 /* Check if this is a packet start with key */
517 if (Flags & DOE_SIO_WITH_KEY)
518 {
519 /* Start the packet with a key */
520 IopStartNextPacketByKey(DeviceObject,
521 (Flags & DOE_SIO_CANCELABLE) ?
522 TRUE : FALSE,
523 CurrentKey);
524 }
525 else if (Flags & DOE_SIO_NO_KEY)
526 {
527 /* Start the packet */
528 IopStartNextPacket(DeviceObject,
529 (Flags & DOE_SIO_CANCELABLE) ?
530 TRUE : FALSE);
531 }
532 }
533
534 /* Decrease the Start I/O count and check if it's 0 now */
535 if (!InterlockedDecrement(&DeviceExtension->StartIoCount))
536 {
537 /* Get the current active key and flags */
538 CurrentKey = DeviceExtension->StartIoKey;
539 CurrentFlags = DeviceExtension->StartIoFlags & (DOE_SIO_WITH_KEY |
540 DOE_SIO_NO_KEY |
541 DOE_SIO_CANCELABLE);
542
543 /* Check if we should still loop */
544 if (!(CurrentFlags & (DOE_SIO_WITH_KEY | DOE_SIO_NO_KEY))) break;
545 }
546 else
547 {
548 /* There are still Start I/Os active, so quit this loop */
549 break;
550 }
551 }
552 }
553
554 /* PUBLIC FUNCTIONS ***********************************************************/
555
556 /*
557 * IoAttachDevice
558 *
559 * Layers a device over the highest device in a device stack.
560 *
561 * Parameters
562 * SourceDevice
563 * Device to be attached.
564 *
565 * TargetDevice
566 * Name of the target device.
567 *
568 * AttachedDevice
569 * Caller storage for the device attached to.
570 *
571 * Status
572 * @implemented
573 */
574 NTSTATUS
575 NTAPI
576 IoAttachDevice(PDEVICE_OBJECT SourceDevice,
577 PUNICODE_STRING TargetDeviceName,
578 PDEVICE_OBJECT *AttachedDevice)
579 {
580 NTSTATUS Status;
581 PFILE_OBJECT FileObject = NULL;
582 PDEVICE_OBJECT TargetDevice = NULL;
583
584 /* Call the helper routine for an attach operation */
585 Status = IopGetDeviceObjectPointer(TargetDeviceName,
586 FILE_READ_ATTRIBUTES,
587 &FileObject,
588 &TargetDevice,
589 IO_ATTACH_DEVICE_API);
590 if (!NT_SUCCESS(Status)) return Status;
591
592 /* Attach the device */
593 Status = IoAttachDeviceToDeviceStackSafe(SourceDevice,
594 TargetDevice,
595 AttachedDevice);
596
597 /* Dereference it */
598 ObDereferenceObject(FileObject);
599 return Status;
600 }
601
602 /*
603 * IoAttachDeviceByPointer
604 *
605 * Status
606 * @implemented
607 */
608 NTSTATUS
609 NTAPI
610 IoAttachDeviceByPointer(IN PDEVICE_OBJECT SourceDevice,
611 IN PDEVICE_OBJECT TargetDevice)
612 {
613 PDEVICE_OBJECT AttachedDevice;
614 NTSTATUS Status = STATUS_SUCCESS;
615
616 /* Do the Attach */
617 AttachedDevice = IoAttachDeviceToDeviceStack(SourceDevice, TargetDevice);
618 if (!AttachedDevice) Status = STATUS_NO_SUCH_DEVICE;
619
620 /* Return the status */
621 return Status;
622 }
623
624 /*
625 * @implemented
626 */
627 PDEVICE_OBJECT
628 NTAPI
629 IoAttachDeviceToDeviceStack(IN PDEVICE_OBJECT SourceDevice,
630 IN PDEVICE_OBJECT TargetDevice)
631 {
632 /* Attach it safely */
633 return IopAttachDeviceToDeviceStackSafe(SourceDevice,
634 TargetDevice,
635 NULL);
636 }
637
638 /*
639 * @implemented
640 */
641 NTSTATUS
642 NTAPI
643 IoAttachDeviceToDeviceStackSafe(IN PDEVICE_OBJECT SourceDevice,
644 IN PDEVICE_OBJECT TargetDevice,
645 IN OUT PDEVICE_OBJECT *AttachedToDeviceObject)
646 {
647 /* Call the internal function */
648 if (!IopAttachDeviceToDeviceStackSafe(SourceDevice,
649 TargetDevice,
650 AttachedToDeviceObject))
651 {
652 /* Nothing found */
653 return STATUS_NO_SUCH_DEVICE;
654 }
655
656 /* Success! */
657 return STATUS_SUCCESS;
658 }
659
660 /*
661 * IoCreateDevice
662 *
663 * Allocates memory for and intializes a device object for use for
664 * a driver.
665 *
666 * Parameters
667 * DriverObject
668 * Driver object passed by IO Manager when the driver was loaded.
669 *
670 * DeviceExtensionSize
671 * Number of bytes for the device extension.
672 *
673 * DeviceName
674 * Unicode name of device.
675 *
676 * DeviceType
677 * Device type of the new device.
678 *
679 * DeviceCharacteristics
680 * Bit mask of device characteristics.
681 *
682 * Exclusive
683 * TRUE if only one thread can access the device at a time.
684 *
685 * DeviceObject
686 * On successful return this parameter is filled by pointer to
687 * allocated device object.
688 *
689 * Status
690 * @implemented
691 */
692 NTSTATUS
693 NTAPI
694 IoCreateDevice(IN PDRIVER_OBJECT DriverObject,
695 IN ULONG DeviceExtensionSize,
696 IN PUNICODE_STRING DeviceName,
697 IN DEVICE_TYPE DeviceType,
698 IN ULONG DeviceCharacteristics,
699 IN BOOLEAN Exclusive,
700 OUT PDEVICE_OBJECT *DeviceObject)
701 {
702 WCHAR AutoNameBuffer[20];
703 UNICODE_STRING AutoName;
704 PDEVICE_OBJECT CreatedDeviceObject;
705 PDEVOBJ_EXTENSION DeviceObjectExtension;
706 OBJECT_ATTRIBUTES ObjectAttributes;
707 NTSTATUS Status;
708 ULONG AlignedDeviceExtensionSize;
709 ULONG TotalSize;
710 HANDLE TempHandle;
711 PAGED_CODE();
712
713 /* Check if we have to generate a name */
714 if (DeviceCharacteristics & FILE_AUTOGENERATED_DEVICE_NAME)
715 {
716 /* Generate it */
717 swprintf(AutoNameBuffer,
718 L"\\Device\\%08lx",
719 InterlockedIncrementUL(&IopDeviceObjectNumber));
720
721 /* Initialize the name */
722 RtlInitUnicodeString(&AutoName, AutoNameBuffer);
723 DeviceName = &AutoName;
724 }
725
726 /* Initialize the Object Attributes */
727 InitializeObjectAttributes(&ObjectAttributes,
728 DeviceName,
729 OBJ_KERNEL_HANDLE,
730 NULL,
731 NULL);
732
733 /* Honor exclusive flag */
734 if (Exclusive) ObjectAttributes.Attributes |= OBJ_EXCLUSIVE;
735
736 /* Create a permanent object for named devices */
737 if (DeviceName) ObjectAttributes.Attributes |= OBJ_PERMANENT;
738
739 /* Align the Extension Size to 8-bytes */
740 AlignedDeviceExtensionSize = (DeviceExtensionSize + 7) &~ 7;
741
742 /* Total Size */
743 TotalSize = AlignedDeviceExtensionSize +
744 sizeof(DEVICE_OBJECT) +
745 sizeof(EXTENDED_DEVOBJ_EXTENSION);
746
747 /* Create the Device Object */
748 *DeviceObject = NULL;
749 Status = ObCreateObject(KernelMode,
750 IoDeviceObjectType,
751 &ObjectAttributes,
752 KernelMode,
753 NULL,
754 TotalSize,
755 0,
756 0,
757 (PVOID*)&CreatedDeviceObject);
758 if (!NT_SUCCESS(Status)) return Status;
759
760 /* Clear the whole Object and extension so we don't null stuff manually */
761 RtlZeroMemory(CreatedDeviceObject, TotalSize);
762
763 /*
764 * Setup the Type and Size. Note that we don't use the aligned size,
765 * because that's only padding for the DevObjExt and not part of the Object.
766 */
767 CreatedDeviceObject->Type = IO_TYPE_DEVICE;
768 CreatedDeviceObject->Size = sizeof(DEVICE_OBJECT) + DeviceExtensionSize;
769
770 /* The kernel extension is after the driver internal extension */
771 DeviceObjectExtension = (PDEVOBJ_EXTENSION)
772 ((ULONG_PTR)(CreatedDeviceObject + 1) +
773 AlignedDeviceExtensionSize);
774
775 /* Set the Type and Size. Question: why is Size 0 on Windows? */
776 DeviceObjectExtension->Type = IO_TYPE_DEVICE_OBJECT_EXTENSION;
777 DeviceObjectExtension->Size = 0;
778
779 /* Link the Object and Extension */
780 DeviceObjectExtension->DeviceObject = CreatedDeviceObject;
781 CreatedDeviceObject->DeviceObjectExtension = DeviceObjectExtension;
782
783 /* Set Device Object Data */
784 CreatedDeviceObject->DeviceType = DeviceType;
785 CreatedDeviceObject->Characteristics = DeviceCharacteristics;
786 CreatedDeviceObject->DeviceExtension = DeviceExtensionSize ?
787 CreatedDeviceObject + 1 :
788 NULL;
789 CreatedDeviceObject->StackSize = 1;
790 CreatedDeviceObject->AlignmentRequirement = 0;
791
792 /* Set the Flags */
793 CreatedDeviceObject->Flags = DO_DEVICE_INITIALIZING;
794 if (Exclusive) CreatedDeviceObject->Flags |= DO_EXCLUSIVE;
795 if (DeviceName) CreatedDeviceObject->Flags |= DO_DEVICE_HAS_NAME;
796
797 /* Attach a Vpb for Disks and Tapes, and create the Device Lock */
798 if ((CreatedDeviceObject->DeviceType == FILE_DEVICE_DISK) ||
799 (CreatedDeviceObject->DeviceType == FILE_DEVICE_VIRTUAL_DISK) ||
800 (CreatedDeviceObject->DeviceType == FILE_DEVICE_CD_ROM) ||
801 (CreatedDeviceObject->DeviceType == FILE_DEVICE_TAPE))
802 {
803 /* Create Vpb */
804 Status = IopCreateVpb(CreatedDeviceObject);
805 if (!NT_SUCCESS(Status))
806 {
807 /* Reference the device object and fail */
808 ObDereferenceObject(DeviceObject);
809 return Status;
810 }
811
812 /* Initialize Lock Event */
813 KeInitializeEvent(&CreatedDeviceObject->DeviceLock,
814 SynchronizationEvent,
815 TRUE);
816 }
817
818 /* Set the right Sector Size */
819 switch (DeviceType)
820 {
821 /* All disk systems */
822 case FILE_DEVICE_DISK_FILE_SYSTEM:
823 case FILE_DEVICE_DISK:
824 case FILE_DEVICE_VIRTUAL_DISK:
825
826 /* The default is 512 bytes */
827 CreatedDeviceObject->SectorSize = 512;
828 break;
829
830 /* CD-ROM file systems */
831 case FILE_DEVICE_CD_ROM_FILE_SYSTEM:
832
833 /* The default is 2048 bytes */
834 CreatedDeviceObject->SectorSize = 2048;
835 }
836
837 /* Create the Device Queue */
838 if ((CreatedDeviceObject->DeviceType == FILE_DEVICE_DISK_FILE_SYSTEM) ||
839 (CreatedDeviceObject->DeviceType == FILE_DEVICE_FILE_SYSTEM) ||
840 (CreatedDeviceObject->DeviceType == FILE_DEVICE_CD_ROM_FILE_SYSTEM) ||
841 (CreatedDeviceObject->DeviceType == FILE_DEVICE_NETWORK_FILE_SYSTEM) ||
842 (CreatedDeviceObject->DeviceType == FILE_DEVICE_TAPE_FILE_SYSTEM))
843 {
844 /* Simple FS Devices, they don't need a real Device Queue */
845 InitializeListHead(&CreatedDeviceObject->Queue.ListEntry);
846 }
847 else
848 {
849 /* An actual Device, initialize its DQ */
850 KeInitializeDeviceQueue(&CreatedDeviceObject->DeviceQueue);
851 }
852
853 /* Insert the Object */
854 Status = ObInsertObject(CreatedDeviceObject,
855 NULL,
856 FILE_READ_DATA | FILE_WRITE_DATA,
857 1,
858 (PVOID*)&CreatedDeviceObject,
859 &TempHandle);
860 if (!NT_SUCCESS(Status)) return Status;
861
862 /* Now do the final linking */
863 ObReferenceObject(DriverObject);
864 ASSERT((DriverObject->Flags & DRVO_UNLOAD_INVOKED) == 0);
865 CreatedDeviceObject->DriverObject = DriverObject;
866 IopEditDeviceList(DriverObject, CreatedDeviceObject, IopAdd);
867
868 /* Close the temporary handle and return to caller */
869 ObCloseHandle(TempHandle, KernelMode);
870 *DeviceObject = CreatedDeviceObject;
871 return STATUS_SUCCESS;
872 }
873
874 /*
875 * IoDeleteDevice
876 *
877 * Status
878 * @implemented
879 */
880 VOID
881 NTAPI
882 IoDeleteDevice(IN PDEVICE_OBJECT DeviceObject)
883 {
884 PIO_TIMER Timer;
885
886 /* Check if the device is registered for shutdown notifications */
887 if (DeviceObject->Flags & DO_SHUTDOWN_REGISTERED)
888 {
889 /* Call the shutdown notifications */
890 IoUnregisterShutdownNotification(DeviceObject);
891 }
892
893 /* Check if it has a timer */
894 Timer = DeviceObject->Timer;
895 if (Timer)
896 {
897 /* Remove it and free it */
898 IopRemoveTimerFromTimerList(Timer);
899 ExFreePoolWithTag(Timer, TAG_IO_TIMER);
900 }
901
902 /* Check if the device has a name */
903 if (DeviceObject->Flags & DO_DEVICE_HAS_NAME)
904 {
905 /* It does, make it temporary so we can remove it */
906 ObMakeTemporaryObject(DeviceObject);
907 }
908
909 /* Set the pending delete flag */
910 IoGetDevObjExtension(DeviceObject)->ExtensionFlags |= DOE_DELETE_PENDING;
911
912 /* Check if the device object can be unloaded */
913 if (!DeviceObject->ReferenceCount) IopUnloadDevice(DeviceObject);
914 }
915
916 /*
917 * IoDetachDevice
918 *
919 * Status
920 * @implemented
921 */
922 VOID
923 NTAPI
924 IoDetachDevice(IN PDEVICE_OBJECT TargetDevice)
925 {
926 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
927
928 /* Sanity check */
929 DeviceExtension = IoGetDevObjExtension(TargetDevice->AttachedDevice);
930 ASSERT(DeviceExtension->AttachedTo == TargetDevice);
931
932 /* Remove the attachment */
933 DeviceExtension->AttachedTo = NULL;
934 TargetDevice->AttachedDevice = NULL;
935
936 /* Check if it's ok to delete this device */
937 if ((IoGetDevObjExtension(TargetDevice)->ExtensionFlags &
938 (DOE_UNLOAD_PENDING | DOE_DELETE_PENDING | DOE_REMOVE_PENDING)) &&
939 !(TargetDevice->ReferenceCount))
940 {
941 /* It is, do it */
942 IopUnloadDevice(TargetDevice);
943 }
944 }
945
946 /*
947 * @implemented
948 */
949 NTSTATUS
950 NTAPI
951 IoEnumerateDeviceObjectList(IN PDRIVER_OBJECT DriverObject,
952 IN PDEVICE_OBJECT *DeviceObjectList,
953 IN ULONG DeviceObjectListSize,
954 OUT PULONG ActualNumberDeviceObjects)
955 {
956 ULONG ActualDevices = 1;
957 PDEVICE_OBJECT CurrentDevice = DriverObject->DeviceObject;
958
959 /* Find out how many devices we'll enumerate */
960 while ((CurrentDevice = CurrentDevice->NextDevice)) ActualDevices++;
961
962 /* Go back to the first */
963 CurrentDevice = DriverObject->DeviceObject;
964
965 /* Start by at least returning this */
966 *ActualNumberDeviceObjects = ActualDevices;
967
968 /* Check if we can support so many */
969 if ((ActualDevices * 4) > DeviceObjectListSize)
970 {
971 /* Fail because the buffer was too small */
972 return STATUS_BUFFER_TOO_SMALL;
973 }
974
975 /* Check if the caller only wanted the size */
976 if (DeviceObjectList)
977 {
978 /* Loop through all the devices */
979 while (ActualDevices)
980 {
981 /* Reference each Device */
982 ObReferenceObject(CurrentDevice);
983
984 /* Add it to the list */
985 *DeviceObjectList = CurrentDevice;
986
987 /* Go to the next one */
988 CurrentDevice = CurrentDevice->NextDevice;
989 ActualDevices--;
990 DeviceObjectList++;
991 }
992 }
993
994 /* Return the status */
995 return STATUS_SUCCESS;
996 }
997
998 /*
999 * IoGetAttachedDevice
1000 *
1001 * Status
1002 * @implemented
1003 */
1004 PDEVICE_OBJECT
1005 NTAPI
1006 IoGetAttachedDevice(PDEVICE_OBJECT DeviceObject)
1007 {
1008 /* Get the last attached device */
1009 while (DeviceObject->AttachedDevice)
1010 {
1011 /* Move to the next one */
1012 DeviceObject = DeviceObject->AttachedDevice;
1013 }
1014
1015 /* Return it */
1016 return DeviceObject;
1017 }
1018
1019 /*
1020 * IoGetAttachedDeviceReference
1021 *
1022 * Status
1023 * @implemented
1024 */
1025 PDEVICE_OBJECT
1026 NTAPI
1027 IoGetAttachedDeviceReference(PDEVICE_OBJECT DeviceObject)
1028 {
1029 /* Reference the Attached Device */
1030 DeviceObject = IoGetAttachedDevice(DeviceObject);
1031 ObReferenceObject(DeviceObject);
1032 return DeviceObject;
1033 }
1034
1035 /*
1036 * @implemented
1037 */
1038 PDEVICE_OBJECT
1039 NTAPI
1040 IoGetDeviceAttachmentBaseRef(IN PDEVICE_OBJECT DeviceObject)
1041 {
1042 /* Reference the lowest attached device */
1043 DeviceObject = IopGetLowestDevice(DeviceObject);
1044 ObReferenceObject(DeviceObject);
1045 return DeviceObject;
1046 }
1047
1048 /*
1049 * IoGetDeviceObjectPointer
1050 *
1051 * Status
1052 * @implemented
1053 */
1054 NTSTATUS
1055 NTAPI
1056 IoGetDeviceObjectPointer(IN PUNICODE_STRING ObjectName,
1057 IN ACCESS_MASK DesiredAccess,
1058 OUT PFILE_OBJECT *FileObject,
1059 OUT PDEVICE_OBJECT *DeviceObject)
1060 {
1061 /* Call the helper routine for a normal operation */
1062 return IopGetDeviceObjectPointer(ObjectName,
1063 DesiredAccess,
1064 FileObject,
1065 DeviceObject,
1066 0);
1067 }
1068
1069 /*
1070 * @implemented
1071 */
1072 NTSTATUS
1073 NTAPI
1074 IoGetDiskDeviceObject(IN PDEVICE_OBJECT FileSystemDeviceObject,
1075 OUT PDEVICE_OBJECT *DiskDeviceObject)
1076 {
1077 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
1078 PVPB Vpb;
1079 KIRQL OldIrql;
1080 NTSTATUS Status;
1081
1082 /* Make sure there's a VPB */
1083 if (!FileSystemDeviceObject->Vpb) return STATUS_INVALID_PARAMETER;
1084
1085 /* Acquire it */
1086 IoAcquireVpbSpinLock(&OldIrql);
1087
1088 /* Get the Device Extension */
1089 DeviceExtension = IoGetDevObjExtension(FileSystemDeviceObject);
1090
1091 /* Make sure this one has a VPB too */
1092 Vpb = DeviceExtension->Vpb;
1093 if (Vpb)
1094 {
1095 /* Make sure that it's mounted */
1096 if ((Vpb->ReferenceCount) &&
1097 (Vpb->Flags & VPB_MOUNTED))
1098 {
1099 /* Return the Disk Device Object */
1100 *DiskDeviceObject = Vpb->RealDevice;
1101
1102 /* Reference it and return success */
1103 ObReferenceObject(Vpb->RealDevice);
1104 Status = STATUS_SUCCESS;
1105 }
1106 else
1107 {
1108 /* It's not, so return failure */
1109 Status = STATUS_VOLUME_DISMOUNTED;
1110 }
1111 }
1112 else
1113 {
1114 /* Fail */
1115 Status = STATUS_INVALID_PARAMETER;
1116 }
1117
1118 /* Release the lock */
1119 IoReleaseVpbSpinLock(OldIrql);
1120 return Status;
1121 }
1122
1123 /*
1124 * @implemented
1125 */
1126 PDEVICE_OBJECT
1127 NTAPI
1128 IoGetLowerDeviceObject(IN PDEVICE_OBJECT DeviceObject)
1129 {
1130 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
1131 PDEVICE_OBJECT LowerDeviceObject = NULL;
1132
1133 /* Make sure it's not getting deleted */
1134 DeviceExtension = IoGetDevObjExtension(DeviceObject);
1135 if (DeviceExtension->ExtensionFlags & (DOE_UNLOAD_PENDING |
1136 DOE_DELETE_PENDING |
1137 DOE_REMOVE_PENDING |
1138 DOE_REMOVE_PROCESSED))
1139 {
1140 /* Get the Lower Device Object */
1141 LowerDeviceObject = DeviceExtension->AttachedTo;
1142
1143 /* Reference it */
1144 ObReferenceObject(LowerDeviceObject);
1145 }
1146
1147 /* Return it */
1148 return LowerDeviceObject;
1149 }
1150
1151 /*
1152 * @implemented
1153 */
1154 PDEVICE_OBJECT
1155 NTAPI
1156 IoGetRelatedDeviceObject(IN PFILE_OBJECT FileObject)
1157 {
1158 PDEVICE_OBJECT DeviceObject = FileObject->DeviceObject;
1159
1160 /* Check if we have a VPB with a device object */
1161 if ((FileObject->Vpb) && (FileObject->Vpb->DeviceObject))
1162 {
1163 /* Then use the DO from the VPB */
1164 ASSERT(!(FileObject->Flags & FO_DIRECT_DEVICE_OPEN));
1165 DeviceObject = FileObject->Vpb->DeviceObject;
1166 }
1167 else if (!(FileObject->Flags & FO_DIRECT_DEVICE_OPEN) &&
1168 (FileObject->DeviceObject->Vpb) &&
1169 (FileObject->DeviceObject->Vpb->DeviceObject))
1170 {
1171 /* The disk device actually has a VPB, so get the DO from there */
1172 DeviceObject = FileObject->DeviceObject->Vpb->DeviceObject;
1173 }
1174 else
1175 {
1176 /* Otherwise, this was a direct open */
1177 DeviceObject = FileObject->DeviceObject;
1178 }
1179
1180 /* Sanity check */
1181 ASSERT(DeviceObject != NULL);
1182
1183 /* Check if we were attached */
1184 if (DeviceObject->AttachedDevice)
1185 {
1186 /* Check if the file object has an extension present */
1187 if (FileObject->Flags & FO_FILE_OBJECT_HAS_EXTENSION)
1188 {
1189 /* Sanity check, direct open files can't have this */
1190 ASSERT(!(FileObject->Flags & FO_DIRECT_DEVICE_OPEN));
1191
1192 /* Check if the extension is really present */
1193 if (FileObject->FileObjectExtension)
1194 {
1195 /* FIXME: Unhandled yet */
1196 DPRINT1("FOEs not supported\n");
1197 KEBUGCHECK(0);
1198 }
1199 }
1200
1201 /* Return the highest attached device */
1202 DeviceObject = IoGetAttachedDevice(DeviceObject);
1203 }
1204
1205 /* Return the DO we found */
1206 return DeviceObject;
1207 }
1208
1209 /*
1210 * @implemented
1211 */
1212 PDEVICE_OBJECT
1213 NTAPI
1214 IoGetBaseFileSystemDeviceObject(IN PFILE_OBJECT FileObject)
1215 {
1216 PDEVICE_OBJECT DeviceObject;
1217
1218 /*
1219 * If the FILE_OBJECT's VPB is defined,
1220 * get the device from it.
1221 */
1222 if ((FileObject->Vpb) && (FileObject->Vpb->DeviceObject))
1223 {
1224 /* Use the VPB's Device Object's */
1225 DeviceObject = FileObject->Vpb->DeviceObject;
1226 }
1227 else if (!(FileObject->Flags & FO_DIRECT_DEVICE_OPEN) &&
1228 (FileObject->DeviceObject->Vpb) &&
1229 (FileObject->DeviceObject->Vpb->DeviceObject))
1230 {
1231 /* Use the VPB's File System Object */
1232 DeviceObject = FileObject->DeviceObject->Vpb->DeviceObject;
1233 }
1234 else
1235 {
1236 /* Use the FO's Device Object */
1237 DeviceObject = FileObject->DeviceObject;
1238 }
1239
1240 /* Return the device object we found */
1241 ASSERT(DeviceObject != NULL);
1242 return DeviceObject;
1243 }
1244
1245 /*
1246 * @implemented
1247 */
1248 NTSTATUS
1249 NTAPI
1250 IoRegisterLastChanceShutdownNotification(IN PDEVICE_OBJECT DeviceObject)
1251 {
1252 PSHUTDOWN_ENTRY Entry;
1253
1254 /* Allocate the shutdown entry */
1255 Entry = ExAllocatePoolWithTag(NonPagedPool,
1256 sizeof(SHUTDOWN_ENTRY),
1257 TAG_SHUTDOWN_ENTRY);
1258 if (!Entry) return STATUS_INSUFFICIENT_RESOURCES;
1259
1260 /* Set the DO */
1261 Entry->DeviceObject = DeviceObject;
1262
1263 /* Insert it into the list */
1264 ExInterlockedInsertHeadList(&LastChanceShutdownListHead,
1265 &Entry->ShutdownList,
1266 &ShutdownListLock);
1267
1268 /* Set the shutdown registered flag */
1269 DeviceObject->Flags |= DO_SHUTDOWN_REGISTERED;
1270 return STATUS_SUCCESS;
1271 }
1272
1273 /*
1274 * @implemented
1275 */
1276 NTSTATUS
1277 NTAPI
1278 IoRegisterShutdownNotification(PDEVICE_OBJECT DeviceObject)
1279 {
1280 PSHUTDOWN_ENTRY Entry;
1281
1282 /* Allocate the shutdown entry */
1283 Entry = ExAllocatePoolWithTag(NonPagedPool,
1284 sizeof(SHUTDOWN_ENTRY),
1285 TAG_SHUTDOWN_ENTRY);
1286 if (!Entry) return STATUS_INSUFFICIENT_RESOURCES;
1287
1288 /* Set the DO */
1289 Entry->DeviceObject = DeviceObject;
1290
1291 /* Insert it into the list */
1292 ExInterlockedInsertHeadList(&ShutdownListHead,
1293 &Entry->ShutdownList,
1294 &ShutdownListLock);
1295
1296 /* Set the shutdown registered flag */
1297 DeviceObject->Flags |= DO_SHUTDOWN_REGISTERED;
1298 return STATUS_SUCCESS;
1299 }
1300
1301 /*
1302 * @implemented
1303 */
1304 VOID
1305 NTAPI
1306 IoUnregisterShutdownNotification(PDEVICE_OBJECT DeviceObject)
1307 {
1308 PSHUTDOWN_ENTRY ShutdownEntry;
1309 PLIST_ENTRY NextEntry;
1310 KIRQL OldIrql;
1311
1312 /* Acquire the shutdown lock and loop the shutdown list */
1313 KeAcquireSpinLock(&ShutdownListLock, &OldIrql);
1314 NextEntry = ShutdownListHead.Flink;
1315 while (NextEntry != &ShutdownListHead)
1316 {
1317 /* Get the entry */
1318 ShutdownEntry = CONTAINING_RECORD(NextEntry,
1319 SHUTDOWN_ENTRY,
1320 ShutdownList);
1321
1322 /* Get if the DO matches */
1323 if (ShutdownEntry->DeviceObject == DeviceObject)
1324 {
1325 /* Remove it from the list */
1326 RemoveEntryList(NextEntry);
1327 NextEntry = NextEntry->Blink;
1328
1329 /* Free the entry */
1330 ExFreePool(ShutdownEntry);
1331 }
1332
1333 /* Go to the next entry */
1334 NextEntry = NextEntry->Flink;
1335 }
1336
1337 /* Now loop the last chance list */
1338 NextEntry = LastChanceShutdownListHead.Flink;
1339 while (NextEntry != &LastChanceShutdownListHead)
1340 {
1341 /* Get the entry */
1342 ShutdownEntry = CONTAINING_RECORD(NextEntry,
1343 SHUTDOWN_ENTRY,
1344 ShutdownList);
1345
1346 /* Get if the DO matches */
1347 if (ShutdownEntry->DeviceObject == DeviceObject)
1348 {
1349 /* Remove it from the list */
1350 RemoveEntryList(NextEntry);
1351 NextEntry = NextEntry->Blink;
1352
1353 /* Free the entry */
1354 ExFreePool(ShutdownEntry);
1355 }
1356
1357 /* Go to the next entry */
1358 NextEntry = NextEntry->Flink;
1359 }
1360
1361 /* Now remove the flag */
1362 DeviceObject->Flags &= ~DO_SHUTDOWN_REGISTERED;
1363 }
1364
1365 /*
1366 * @implemented
1367 */
1368 VOID
1369 NTAPI
1370 IoSetStartIoAttributes(IN PDEVICE_OBJECT DeviceObject,
1371 IN BOOLEAN DeferredStartIo,
1372 IN BOOLEAN NonCancelable)
1373 {
1374 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
1375
1376 /* Get the Device Extension */
1377 DeviceExtension = IoGetDevObjExtension(DeviceObject);
1378
1379 /* Set the flags the caller requested */
1380 DeviceExtension->StartIoFlags |= (DeferredStartIo) ? DOE_SIO_DEFERRED : 0;
1381 DeviceExtension->StartIoFlags |= (NonCancelable) ? DOE_SIO_NO_CANCEL : 0;
1382 }
1383
1384 /*
1385 * @implemented
1386 */
1387 VOID
1388 NTAPI
1389 IoStartNextPacketByKey(IN PDEVICE_OBJECT DeviceObject,
1390 IN BOOLEAN Cancelable,
1391 IN ULONG Key)
1392 {
1393 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
1394
1395 /* Get the Device Extension */
1396 DeviceExtension = IoGetDevObjExtension(DeviceObject);
1397
1398 /* Check if deferred start was requested */
1399 if (DeviceExtension->StartIoFlags & DOE_SIO_DEFERRED)
1400 {
1401 /* Call our internal function to handle the defered case */
1402 IopStartNextPacketByKeyEx(DeviceObject,
1403 Key,
1404 DOE_SIO_WITH_KEY |
1405 (Cancelable) ? DOE_SIO_CANCELABLE : 0);
1406 }
1407 else
1408 {
1409 /* Call the normal routine */
1410 IopStartNextPacketByKey(DeviceObject, Cancelable, Key);
1411 }
1412 }
1413
1414 /*
1415 * @implemented
1416 */
1417 VOID
1418 NTAPI
1419 IoStartNextPacket(IN PDEVICE_OBJECT DeviceObject,
1420 IN BOOLEAN Cancelable)
1421 {
1422 PEXTENDED_DEVOBJ_EXTENSION DeviceExtension;
1423
1424 /* Get the Device Extension */
1425 DeviceExtension = IoGetDevObjExtension(DeviceObject);
1426
1427 /* Check if deferred start was requested */
1428 if (DeviceExtension->StartIoFlags & DOE_SIO_DEFERRED)
1429 {
1430 /* Call our internal function to handle the defered case */
1431 IopStartNextPacketByKeyEx(DeviceObject,
1432 0,
1433 DOE_SIO_NO_KEY |
1434 (Cancelable) ? DOE_SIO_CANCELABLE : 0);
1435 }
1436 else
1437 {
1438 /* Call the normal routine */
1439 IopStartNextPacket(DeviceObject, Cancelable);
1440 }
1441 }
1442
1443 /*
1444 * @implemented
1445 */
1446 VOID
1447 NTAPI
1448 IoStartPacket(IN PDEVICE_OBJECT DeviceObject,
1449 IN PIRP Irp,
1450 IN PULONG Key,
1451 IN PDRIVER_CANCEL CancelFunction)
1452 {
1453 BOOLEAN Stat;
1454 KIRQL OldIrql, CancelIrql;
1455
1456 /* Raise to dispatch level */
1457 KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
1458
1459 /* Check if we should acquire the cancel lock */
1460 if (CancelFunction)
1461 {
1462 /* Acquire and set it */
1463 IoAcquireCancelSpinLock(&CancelIrql);
1464 Irp->CancelRoutine = CancelFunction;
1465 }
1466
1467 /* Check if we have a key */
1468 if (Key)
1469 {
1470 /* Insert by key */
1471 Stat = KeInsertByKeyDeviceQueue(&DeviceObject->DeviceQueue,
1472 &Irp->Tail.Overlay.DeviceQueueEntry,
1473 *Key);
1474 }
1475 else
1476 {
1477 /* Insert without a key */
1478 Stat = KeInsertDeviceQueue(&DeviceObject->DeviceQueue,
1479 &Irp->Tail.Overlay.DeviceQueueEntry);
1480 }
1481
1482 /* Check if this was a first insert */
1483 if (!Stat)
1484 {
1485 /* Set the IRP */
1486 DeviceObject->CurrentIrp = Irp;
1487
1488 /* Check if this is a cancelable packet */
1489 if (CancelFunction)
1490 {
1491 /* Check if the caller requested no cancellation */
1492 if (IoGetDevObjExtension(DeviceObject)->StartIoFlags &
1493 DOE_SIO_NO_CANCEL)
1494 {
1495 /* He did, so remove the cancel routine */
1496 Irp->CancelRoutine = NULL;
1497 }
1498
1499 /* Release the cancel lock */
1500 IoReleaseCancelSpinLock(OldIrql);
1501 }
1502
1503 /* Call the Start I/O function */
1504 DeviceObject->DriverObject->DriverStartIo(DeviceObject, Irp);
1505 }
1506 else
1507 {
1508 /* The packet was inserted... check if we have a cancel function */
1509 if (CancelFunction)
1510 {
1511 /* Check if the IRP got cancelled */
1512 if (Irp->Cancel)
1513 {
1514 /*
1515 * Set the cancel IRQL, clear the currnet cancel routine and
1516 * call ours
1517 */
1518 Irp->CancelIrql = CancelIrql;
1519 Irp->CancelRoutine = NULL;
1520 CancelFunction(DeviceObject, Irp);
1521 }
1522 else
1523 {
1524 /* Otherwise, release the lock */
1525 IoReleaseCancelSpinLock(CancelIrql);
1526 }
1527 }
1528 }
1529
1530 /* Return back to previous IRQL */
1531 KeLowerIrql(OldIrql);
1532 }
1533
1534 /* EOF */