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