xref: /reactos/drivers/storage/class/classpnp/power.c (revision ebaf247c)
1 /*++
2 
3 Copyright (C) Microsoft Corporation, 1991 - 1999
4 
5 Module Name:
6 
7     class.c
8 
9 Abstract:
10 
11     SCSI class driver routines
12 
13 Environment:
14 
15     kernel mode only
16 
17 Notes:
18 
19 
20 Revision History:
21 
22 --*/
23 
24 #include "classp.h"
25 
26 #define CLASS_TAG_POWER     'WLcS'
27 
28 NTSTATUS
29 NTAPI
30 ClasspPowerHandler(
31     IN PDEVICE_OBJECT DeviceObject,
32     IN PIRP Irp,
33     IN CLASS_POWER_OPTIONS Options
34     );
35 
36 IO_COMPLETION_ROUTINE ClasspPowerDownCompletion;
37 
38 IO_COMPLETION_ROUTINE ClasspPowerUpCompletion;
39 
40 VOID
41 NTAPI
42 RetryPowerRequest(
43     PDEVICE_OBJECT DeviceObject,
44     PIRP Irp,
45     PCLASS_POWER_CONTEXT Context
46     );
47 
48 NTSTATUS
49 NTAPI
50 ClasspStartNextPowerIrpCompletion(
51     IN PDEVICE_OBJECT DeviceObject,
52     IN PIRP Irp,
53     IN PVOID Context
54     );
55 
56 
57 /*++////////////////////////////////////////////////////////////////////////////
58 
59 ClassDispatchPower()
60 
61 Routine Description:
62 
63     This routine acquires the removelock for the irp and then calls the
64     appropriate power callback.
65 
66 Arguments:
67 
68     DeviceObject -
69     Irp -
70 
71 Return Value:
72 
73 --*/
74 NTSTATUS
75 NTAPI
76 ClassDispatchPower(
77     IN PDEVICE_OBJECT DeviceObject,
78     IN PIRP Irp
79     )
80 {
81     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
82     ULONG isRemoved;
83 
84     //
85     // NOTE: This code may be called at PASSIVE or DISPATCH, depending
86     //       upon the device object it is being called for.
87     //       don't do anything that would break under either circumstance.
88     //
89 
90     isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
91 
92     if(isRemoved) {
93         ClassReleaseRemoveLock(DeviceObject, Irp);
94         Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
95         PoStartNextPowerIrp(Irp);
96         ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
97         return STATUS_DEVICE_DOES_NOT_EXIST;
98     }
99 
100     return commonExtension->DevInfo->ClassPowerDevice(DeviceObject, Irp);
101 } // end ClassDispatchPower()
102 
103 /*++////////////////////////////////////////////////////////////////////////////
104 
105 ClasspPowerUpCompletion()
106 
107 Routine Description:
108 
109     This routine is used for intermediate completion of a power up request.
110     PowerUp requires four requests to be sent to the lower driver in sequence.
111 
112         * The queue is "power locked" to ensure that the class driver power-up
113           work can be done before request processing resumes.
114 
115         * The power irp is sent down the stack for any filter drivers and the
116           port driver to return power and resume command processing for the
117           device.  Since the queue is locked, no queued irps will be sent
118           immediately.
119 
120         * A start unit command is issued to the device with appropriate flags
121           to override the "power locked" queue.
122 
123         * The queue is "power unlocked" to start processing requests again.
124 
125     This routine uses the function in the srb which just completed to determine
126     which state it is in.
127 
128 Arguments:
129 
130     DeviceObject - the device object being powered up
131 
132     Irp - the IO_REQUEST_PACKET containing the power request
133 
134     Srb - the SRB used to perform port/class operations.
135 
136 Return Value:
137 
138     STATUS_MORE_PROCESSING_REQUIRED or
139     STATUS_SUCCESS
140 
141 --*/
142 NTSTATUS
143 NTAPI
144 ClasspPowerUpCompletion(
145     IN PDEVICE_OBJECT DeviceObject,
146     IN PIRP Irp,
147     IN PVOID CompletionContext
148     )
149 {
150     PCLASS_POWER_CONTEXT context = CompletionContext;
151     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
152     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
153 
154     PIO_STACK_LOCATION currentStack = IoGetCurrentIrpStackLocation(Irp);
155     PIO_STACK_LOCATION nextStack = IoGetNextIrpStackLocation(Irp);
156 
157 
158     NTSTATUS status = STATUS_MORE_PROCESSING_REQUIRED;
159 
160     DebugPrint((1, "ClasspPowerUpCompletion: Device Object %p, Irp %p, "
161                    "Context %p\n",
162                 DeviceObject, Irp, context));
163 
164     ASSERT(!TEST_FLAG(context->Srb.SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
165     ASSERT(!TEST_FLAG(context->Srb.SrbFlags, SRB_FLAGS_PORT_DRIVER_ALLOCSENSE));
166     ASSERT(context->Options.PowerDown == FALSE);
167     ASSERT(context->Options.HandleSpinUp);
168 
169     if(Irp->PendingReturned) {
170         IoMarkIrpPending(Irp);
171     }
172 
173     context->PowerChangeState.PowerUp++;
174 
175     switch(context->PowerChangeState.PowerUp) {
176 
177         case PowerUpDeviceLocked: {
178 
179             DebugPrint((1, "(%p)\tPreviously sent power lock\n", Irp));
180 
181             //
182             // Issue the actual power request to the lower driver.
183             //
184 
185             IoCopyCurrentIrpStackLocationToNext(Irp);
186 
187             //
188             // If the lock wasn't successful then just bail out on the power
189             // request unless we can ignore failed locks
190             //
191 
192             if ((context->Options.LockQueue != FALSE) &&
193                (!NT_SUCCESS(Irp->IoStatus.Status))) {
194 
195                 DebugPrint((1, "(%p)\tIrp status was %lx\n",
196                             Irp, Irp->IoStatus.Status));
197                 DebugPrint((1, "(%p)\tSrb status was %lx\n",
198                             Irp, context->Srb.SrbStatus));
199 
200                 //
201                 // Lock was not successful - throw down the power IRP
202                 // by itself and don't try to spin up the drive or unlock
203                 // the queue.
204                 //
205 
206                 context->InUse = FALSE;
207                 context = NULL;
208 
209                 //
210                 // Set the new power state
211                 //
212 
213                 fdoExtension->DevicePowerState =
214                     currentStack->Parameters.Power.State.DeviceState;
215 
216                 Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
217 
218                 IoCopyCurrentIrpStackLocationToNext(Irp);
219 
220                 IoSetCompletionRoutine(Irp,
221                                        ClasspStartNextPowerIrpCompletion,
222                                        NULL,
223                                        TRUE,
224                                        TRUE,
225                                        TRUE);
226 
227                 //
228                 // Indicate to Po that we've been successfully powered up so
229                 // it can do it's notification stuff.
230                 //
231 
232                 PoSetPowerState(DeviceObject,
233                                 currentStack->Parameters.Power.Type,
234                                 currentStack->Parameters.Power.State);
235 
236                 PoCallDriver(commonExtension->LowerDeviceObject, Irp);
237 
238                 ClassReleaseRemoveLock(commonExtension->DeviceObject,
239                                        Irp);
240 
241                 return STATUS_MORE_PROCESSING_REQUIRED;
242 
243             } else {
244                 context->QueueLocked = (UCHAR) context->Options.LockQueue;
245             }
246 
247             Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
248 
249             context->PowerChangeState.PowerUp = PowerUpDeviceLocked;
250 
251             IoSetCompletionRoutine(Irp,
252                                    ClasspPowerUpCompletion,
253                                    context,
254                                    TRUE,
255                                    TRUE,
256                                    TRUE);
257 
258             status = PoCallDriver(commonExtension->LowerDeviceObject, Irp);
259 
260             DebugPrint((2, "(%p)\tPoCallDriver returned %lx\n", Irp, status));
261             break;
262         }
263 
264         case PowerUpDeviceOn: {
265 
266             PCDB cdb;
267 
268             if(NT_SUCCESS(Irp->IoStatus.Status)) {
269 
270                 DebugPrint((1, "(%p)\tSending start unit to device\n", Irp));
271 
272                 //
273                 // Issue the start unit command to the device.
274                 //
275 
276                 context->Srb.Length = sizeof(SCSI_REQUEST_BLOCK);
277                 context->Srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
278 
279                 context->Srb.SrbStatus = context->Srb.ScsiStatus = 0;
280                 context->Srb.DataTransferLength = 0;
281 
282                 context->Srb.TimeOutValue = START_UNIT_TIMEOUT;
283 
284                 context->Srb.SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER |
285                                         SRB_FLAGS_DISABLE_AUTOSENSE |
286                                         SRB_FLAGS_DISABLE_SYNCH_TRANSFER |
287                                         SRB_FLAGS_NO_QUEUE_FREEZE;
288 
289                 if(context->Options.LockQueue) {
290                     SET_FLAG(context->Srb.SrbFlags, SRB_FLAGS_BYPASS_LOCKED_QUEUE);
291                 }
292 
293                 context->Srb.CdbLength = 6;
294 
295                 cdb = (PCDB) (context->Srb.Cdb);
296                 RtlZeroMemory(cdb, sizeof(CDB));
297 
298 
299                 cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
300                 cdb->START_STOP.Start = 1;
301 
302                 context->PowerChangeState.PowerUp = PowerUpDeviceOn;
303 
304                 IoSetCompletionRoutine(Irp,
305                                        ClasspPowerUpCompletion,
306                                        context,
307                                        TRUE,
308                                        TRUE,
309                                        TRUE);
310 
311                 nextStack->Parameters.Scsi.Srb = &(context->Srb);
312                 nextStack->MajorFunction = IRP_MJ_SCSI;
313 
314                 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
315 
316                 DebugPrint((2, "(%p)\tIoCallDriver returned %lx\n", Irp, status));
317 
318             } else {
319 
320                 //
321                 // we're done.
322                 //
323 
324                 context->FinalStatus = Irp->IoStatus.Status;
325                 goto ClasspPowerUpCompletionFailure;
326             }
327 
328             break;
329         }
330 
331         case PowerUpDeviceStarted: { // 3
332 
333             //
334             // First deal with an error if one occurred.
335             //
336 
337             if(SRB_STATUS(context->Srb.SrbStatus) != SRB_STATUS_SUCCESS) {
338 
339                 BOOLEAN retry;
340 
341                 DebugPrint((1, "%p\tError occured when issuing START_UNIT "
342                             "command to device. Srb %p, Status %x\n",
343                             Irp,
344                             &context->Srb,
345                             context->Srb.SrbStatus));
346 
347                 ASSERT(!(TEST_FLAG(context->Srb.SrbStatus,
348                                    SRB_STATUS_QUEUE_FROZEN)));
349                 ASSERT(context->Srb.Function == SRB_FUNCTION_EXECUTE_SCSI);
350 
351                 context->RetryInterval = 0;
352 
353                 retry = ClassInterpretSenseInfo(
354                             commonExtension->DeviceObject,
355                             &context->Srb,
356                             IRP_MJ_SCSI,
357                             IRP_MJ_POWER,
358                             MAXIMUM_RETRIES - context->RetryCount,
359                             &status,
360                             &context->RetryInterval);
361 
362                 if ((retry != FALSE) && (context->RetryCount-- != 0)) {
363 
364                     DebugPrint((1, "(%p)\tRetrying failed request\n", Irp));
365 
366                     //
367                     // Decrement the state so we come back through here the
368                     // next time.
369                     //
370 
371                     context->PowerChangeState.PowerUp--;
372 
373                     RetryPowerRequest(commonExtension->DeviceObject,
374                                       Irp,
375                                       context);
376 
377                     break;
378 
379                 }
380 
381                 // reset retries
382                 context->RetryCount = MAXIMUM_RETRIES;
383 
384             }
385 
386 ClasspPowerUpCompletionFailure:
387 
388             DebugPrint((1, "(%p)\tPreviously spun device up\n", Irp));
389 
390             if (context->QueueLocked) {
391                 DebugPrint((1, "(%p)\tUnlocking queue\n", Irp));
392 
393                 context->Srb.Function = SRB_FUNCTION_UNLOCK_QUEUE;
394                 context->Srb.SrbFlags = SRB_FLAGS_BYPASS_LOCKED_QUEUE;
395                 context->Srb.SrbStatus = context->Srb.ScsiStatus = 0;
396                 context->Srb.DataTransferLength = 0;
397 
398                 nextStack->Parameters.Scsi.Srb = &(context->Srb);
399                 nextStack->MajorFunction = IRP_MJ_SCSI;
400 
401                 context->PowerChangeState.PowerUp = PowerUpDeviceStarted;
402 
403                 IoSetCompletionRoutine(Irp,
404                                        ClasspPowerUpCompletion,
405                                        context,
406                                        TRUE,
407                                        TRUE,
408                                        TRUE);
409 
410                 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
411                 DebugPrint((1, "(%p)\tIoCallDriver returned %lx\n",
412                             Irp, status));
413                 break;
414             }
415 
416             // Fall-through to next case...
417 
418         }
419 
420         case PowerUpDeviceUnlocked: {
421 
422             //
423             // This is the end of the dance.  Free the srb and complete the
424             // request finally.  We're ignoring possible intermediate
425             // error conditions ....
426             //
427 
428             if (context->QueueLocked) {
429                 DebugPrint((1, "(%p)\tPreviously unlocked queue\n", Irp));
430                 ASSERT(NT_SUCCESS(Irp->IoStatus.Status));
431                 ASSERT(context->Srb.SrbStatus == SRB_STATUS_SUCCESS);
432             } else {
433                 DebugPrint((1, "(%p)\tFall-through (queue not locked)\n", Irp));
434             }
435 
436             DebugPrint((1, "(%p)\tFreeing srb and completing\n", Irp));
437             context->InUse = FALSE;
438 
439             status = context->FinalStatus;
440             Irp->IoStatus.Status = status;
441 
442             context = NULL;
443 
444             //
445             // Set the new power state
446             //
447 
448             if(NT_SUCCESS(status)) {
449                 fdoExtension->DevicePowerState =
450                     currentStack->Parameters.Power.State.DeviceState;
451             }
452 
453             //
454             // Indicate to Po that we've been successfully powered up so
455             // it can do it's notification stuff.
456             //
457 
458             PoSetPowerState(DeviceObject,
459                             currentStack->Parameters.Power.Type,
460                             currentStack->Parameters.Power.State);
461 
462             DebugPrint((1, "(%p)\tStarting next power irp\n", Irp));
463             ClassReleaseRemoveLock(DeviceObject, Irp);
464             PoStartNextPowerIrp(Irp);
465 
466             return status;
467         }
468 
469         case PowerUpDeviceInitial: {
470             NT_ASSERT(context->PowerChangeState.PowerUp != PowerUpDeviceInitial);
471             break;
472         }
473     }
474 
475     return STATUS_MORE_PROCESSING_REQUIRED;
476 } // end ClasspPowerUpCompletion()
477 
478 /*++////////////////////////////////////////////////////////////////////////////
479 
480 ClasspPowerDownCompletion()
481 
482 Routine Description:
483 
484     This routine is used for intermediate completion of a power up request.
485     PowerUp requires four requests to be sent to the lower driver in sequence.
486 
487         * The queue is "power locked" to ensure that the class driver power-up
488           work can be done before request processing resumes.
489 
490         * The power irp is sent down the stack for any filter drivers and the
491           port driver to return power and resume command processing for the
492           device.  Since the queue is locked, no queued irps will be sent
493           immediately.
494 
495         * A start unit command is issued to the device with appropriate flags
496           to override the "power locked" queue.
497 
498         * The queue is "power unlocked" to start processing requests again.
499 
500     This routine uses the function in the srb which just completed to determine
501     which state it is in.
502 
503 Arguments:
504 
505     DeviceObject - the device object being powered up
506 
507     Irp - the IO_REQUEST_PACKET containing the power request
508 
509     Srb - the SRB used to perform port/class operations.
510 
511 Return Value:
512 
513     STATUS_MORE_PROCESSING_REQUIRED or
514     STATUS_SUCCESS
515 
516 --*/
517 NTSTATUS
518 NTAPI
519 ClasspPowerDownCompletion(
520     IN PDEVICE_OBJECT DeviceObject,
521     IN PIRP Irp,
522     IN PVOID CompletionContext
523     )
524 {
525     PCLASS_POWER_CONTEXT context = CompletionContext;
526     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
527     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
528 
529     PIO_STACK_LOCATION currentStack = IoGetCurrentIrpStackLocation(Irp);
530     PIO_STACK_LOCATION nextStack = IoGetNextIrpStackLocation(Irp);
531 
532     NTSTATUS status = STATUS_MORE_PROCESSING_REQUIRED;
533 
534     DebugPrint((1, "ClasspPowerDownCompletion: Device Object %p, "
535                    "Irp %p, Context %p\n",
536                 DeviceObject, Irp, context));
537 
538     ASSERT(!TEST_FLAG(context->Srb.SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
539     ASSERT(!TEST_FLAG(context->Srb.SrbFlags, SRB_FLAGS_PORT_DRIVER_ALLOCSENSE));
540     ASSERT(context->Options.PowerDown == TRUE);
541     ASSERT(context->Options.HandleSpinDown);
542 
543     if(Irp->PendingReturned) {
544         IoMarkIrpPending(Irp);
545     }
546 
547     context->PowerChangeState.PowerDown2++;
548 
549     switch(context->PowerChangeState.PowerDown2) {
550 
551         case PowerDownDeviceLocked2: {
552 
553             PCDB cdb;
554 
555             DebugPrint((1, "(%p)\tPreviously sent power lock\n", Irp));
556 
557             if ((context->Options.LockQueue != FALSE) &&
558                (!NT_SUCCESS(Irp->IoStatus.Status))) {
559 
560                 DebugPrint((1, "(%p)\tIrp status was %lx\n",
561                             Irp,
562                             Irp->IoStatus.Status));
563                 DebugPrint((1, "(%p)\tSrb status was %lx\n",
564                             Irp,
565                             context->Srb.SrbStatus));
566 
567                 Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
568 
569                 //
570                 // Lock was not successful - throw down the power IRP
571                 // by itself and don't try to spin down the drive or unlock
572                 // the queue.
573                 //
574 
575                 context->InUse = FALSE;
576                 context = NULL;
577 
578                 //
579                 // Set the new power state
580                 //
581 
582                 fdoExtension->DevicePowerState =
583                     currentStack->Parameters.Power.State.DeviceState;
584 
585                 //
586                 // Indicate to Po that we've been successfully powered down
587                 // so it can do it's notification stuff.
588                 //
589 
590                 IoCopyCurrentIrpStackLocationToNext(Irp);
591                 IoSetCompletionRoutine(Irp,
592                                        ClasspStartNextPowerIrpCompletion,
593                                        NULL,
594                                        TRUE,
595                                        TRUE,
596                                        TRUE);
597 
598                 PoSetPowerState(DeviceObject,
599                                 currentStack->Parameters.Power.Type,
600                                 currentStack->Parameters.Power.State);
601 
602                 fdoExtension->PowerDownInProgress = FALSE;
603 
604                 PoCallDriver(commonExtension->LowerDeviceObject, Irp);
605 
606                 ClassReleaseRemoveLock(commonExtension->DeviceObject,
607                                        Irp);
608 
609                 return STATUS_MORE_PROCESSING_REQUIRED;
610 
611             } else {
612                 context->QueueLocked = (UCHAR) context->Options.LockQueue;
613             }
614 
615             if (!TEST_FLAG(fdoExtension->PrivateFdoData->HackFlags,
616                            FDO_HACK_NO_SYNC_CACHE)) {
617 
618                 //
619                 // send SCSIOP_SYNCHRONIZE_CACHE
620                 //
621 
622                 context->Srb.Length = sizeof(SCSI_REQUEST_BLOCK);
623                 context->Srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
624 
625                 context->Srb.TimeOutValue = fdoExtension->TimeOutValue;
626 
627                 context->Srb.SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER |
628                                         SRB_FLAGS_DISABLE_AUTOSENSE |
629                                         SRB_FLAGS_DISABLE_SYNCH_TRANSFER |
630                                         SRB_FLAGS_NO_QUEUE_FREEZE |
631                                         SRB_FLAGS_BYPASS_LOCKED_QUEUE;
632 
633                 context->Srb.SrbStatus = context->Srb.ScsiStatus = 0;
634                 context->Srb.DataTransferLength = 0;
635 
636                 context->Srb.CdbLength = 10;
637 
638                 cdb = (PCDB) context->Srb.Cdb;
639 
640                 RtlZeroMemory(cdb, sizeof(CDB));
641                 cdb->SYNCHRONIZE_CACHE10.OperationCode = SCSIOP_SYNCHRONIZE_CACHE;
642 
643                 IoSetCompletionRoutine(Irp,
644                                        ClasspPowerDownCompletion,
645                                        context,
646                                        TRUE,
647                                        TRUE,
648                                        TRUE);
649 
650                 nextStack->Parameters.Scsi.Srb = &(context->Srb);
651                 nextStack->MajorFunction = IRP_MJ_SCSI;
652 
653                 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
654 
655                 DebugPrint((1, "(%p)\tIoCallDriver returned %lx\n", Irp, status));
656                 break;
657 
658             } else {
659 
660                 DebugPrint((1, "(%p)\tPower Down: not sending SYNCH_CACHE\n",
661                             DeviceObject));
662                 context->PowerChangeState.PowerDown2++;
663                 context->Srb.SrbStatus = SRB_STATUS_SUCCESS;
664                 // and fall through....
665             }
666             // no break in case the device doesn't like synch_cache commands
667 
668         }
669 
670         case PowerDownDeviceFlushed2: {
671 
672             PCDB cdb;
673 
674             DebugPrint((1, "(%p)\tPreviously send SCSIOP_SYNCHRONIZE_CACHE\n",
675                         Irp));
676 
677             //
678             // SCSIOP_SYNCHRONIZE_CACHE was sent
679             //
680 
681             if(SRB_STATUS(context->Srb.SrbStatus) != SRB_STATUS_SUCCESS) {
682 
683                 BOOLEAN retry;
684 
685                 DebugPrint((1, "(%p)\tError occured when issuing "
686                             "SYNCHRONIZE_CACHE command to device. "
687                             "Srb %p, Status %lx\n",
688                             Irp,
689                             &context->Srb,
690                             context->Srb.SrbStatus));
691 
692                 ASSERT(!(TEST_FLAG(context->Srb.SrbStatus,
693                                    SRB_STATUS_QUEUE_FROZEN)));
694                 ASSERT(context->Srb.Function == SRB_FUNCTION_EXECUTE_SCSI);
695 
696                 context->RetryInterval = 0;
697                 retry = ClassInterpretSenseInfo(
698                             commonExtension->DeviceObject,
699                             &context->Srb,
700                             IRP_MJ_SCSI,
701                             IRP_MJ_POWER,
702                             MAXIMUM_RETRIES - context->RetryCount,
703                             &status,
704                             &context->RetryInterval);
705 
706                 if ((retry != FALSE) && (context->RetryCount-- != 0)) {
707 
708                         DebugPrint((1, "(%p)\tRetrying failed request\n", Irp));
709 
710                         //
711                         // decrement the state so we come back through here
712                         // the next time.
713                         //
714 
715                         context->PowerChangeState.PowerDown2--;
716                         RetryPowerRequest(commonExtension->DeviceObject,
717                                           Irp,
718                                           context);
719                         break;
720                 }
721 
722                 DebugPrint((1, "(%p)\tSYNCHRONIZE_CACHE not retried\n", Irp));
723                 context->RetryCount = MAXIMUM_RETRIES;
724 
725             } // end !SRB_STATUS_SUCCESS
726 
727             //
728             // note: we are purposefully ignoring any errors.  if the drive
729             //       doesn't support a synch_cache, then we're up a creek
730             //       anyways.
731             //
732 
733             DebugPrint((1, "(%p)\tSending stop unit to device\n", Irp));
734 
735             //
736             // Issue the start unit command to the device.
737             //
738 
739             context->Srb.Length = sizeof(SCSI_REQUEST_BLOCK);
740             context->Srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
741 
742             context->Srb.TimeOutValue = START_UNIT_TIMEOUT;
743 
744             context->Srb.SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER |
745                                     SRB_FLAGS_DISABLE_AUTOSENSE |
746                                     SRB_FLAGS_DISABLE_SYNCH_TRANSFER |
747                                     SRB_FLAGS_NO_QUEUE_FREEZE |
748                                     SRB_FLAGS_BYPASS_LOCKED_QUEUE;
749 
750             context->Srb.SrbStatus = context->Srb.ScsiStatus = 0;
751             context->Srb.DataTransferLength = 0;
752 
753             context->Srb.CdbLength = 6;
754 
755             cdb = (PCDB) context->Srb.Cdb;
756             RtlZeroMemory(cdb, sizeof(CDB));
757 
758             cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
759             cdb->START_STOP.Start = 0;
760             cdb->START_STOP.Immediate = 1;
761 
762             IoSetCompletionRoutine(Irp,
763                                    ClasspPowerDownCompletion,
764                                    context,
765                                    TRUE,
766                                    TRUE,
767                                    TRUE);
768 
769             nextStack->Parameters.Scsi.Srb = &(context->Srb);
770             nextStack->MajorFunction = IRP_MJ_SCSI;
771 
772             status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
773 
774             DebugPrint((1, "(%p)\tIoCallDriver returned %lx\n", Irp, status));
775             break;
776 
777         }
778 
779         case PowerDownDeviceStopped2: {
780 
781             BOOLEAN ignoreError = TRUE;
782 
783             //
784             // stop was sent
785             //
786 
787             if(SRB_STATUS(context->Srb.SrbStatus) != SRB_STATUS_SUCCESS) {
788 
789                 BOOLEAN retry;
790 
791                 DebugPrint((1, "(%p)\tError occured when issuing STOP_UNIT "
792                             "command to device. Srb %p, Status %lx\n",
793                             Irp,
794                             &context->Srb,
795                             context->Srb.SrbStatus));
796 
797                 ASSERT(!(TEST_FLAG(context->Srb.SrbStatus,
798                                    SRB_STATUS_QUEUE_FROZEN)));
799                 ASSERT(context->Srb.Function == SRB_FUNCTION_EXECUTE_SCSI);
800 
801                 context->RetryInterval = 0;
802                 retry = ClassInterpretSenseInfo(
803                             commonExtension->DeviceObject,
804                             &context->Srb,
805                             IRP_MJ_SCSI,
806                             IRP_MJ_POWER,
807                             MAXIMUM_RETRIES - context->RetryCount,
808                             &status,
809                             &context->RetryInterval);
810 
811                 if ((retry != FALSE) && (context->RetryCount-- != 0)) {
812 
813                         DebugPrint((1, "(%p)\tRetrying failed request\n", Irp));
814 
815                         //
816                         // decrement the state so we come back through here
817                         // the next time.
818                         //
819 
820                         context->PowerChangeState.PowerDown2--;
821                         RetryPowerRequest(commonExtension->DeviceObject,
822                                           Irp,
823                                           context);
824                         break;
825                 }
826 
827                 DebugPrint((1, "(%p)\tSTOP_UNIT not retried\n", Irp));
828                 context->RetryCount = MAXIMUM_RETRIES;
829 
830             } // end !SRB_STATUS_SUCCESS
831 
832 
833             DebugPrint((1, "(%p)\tPreviously sent stop unit\n", Irp));
834 
835             //
836             // some operations, such as a physical format in progress,
837             // should not be ignored and should fail the power operation.
838             //
839 
840             if (!NT_SUCCESS(status)) {
841 
842                 PSENSE_DATA senseBuffer = context->Srb.SenseInfoBuffer;
843 
844                 if (TEST_FLAG(context->Srb.SrbStatus,
845                               SRB_STATUS_AUTOSENSE_VALID) &&
846                     ((senseBuffer->SenseKey & 0xf) == SCSI_SENSE_NOT_READY) &&
847                     (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY) &&
848                     (senseBuffer->AdditionalSenseCodeQualifier == SCSI_SENSEQ_FORMAT_IN_PROGRESS)
849                     ) {
850                     ignoreError = FALSE;
851                     context->FinalStatus = STATUS_DEVICE_BUSY;
852                     status = context->FinalStatus;
853                 }
854 
855             }
856 
857             if (NT_SUCCESS(status) || ignoreError) {
858 
859                 //
860                 // Issue the actual power request to the lower driver.
861                 //
862 
863                 Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
864 
865                 IoCopyCurrentIrpStackLocationToNext(Irp);
866 
867                 IoSetCompletionRoutine(Irp,
868                                        ClasspPowerDownCompletion,
869                                        context,
870                                        TRUE,
871                                        TRUE,
872                                        TRUE);
873 
874                 status = PoCallDriver(commonExtension->LowerDeviceObject, Irp);
875 
876                 DebugPrint((1, "(%p)\tPoCallDriver returned %lx\n", Irp, status));
877                 break;
878             }
879 
880             // else fall through w/o sending the power irp, since the device
881             // is reporting an error that would be "really bad" to power down
882             // during.
883 
884         }
885 
886         case PowerDownDeviceOff2: {
887 
888             //
889             // SpinDown request completed ... whether it succeeded or not is
890             // another matter entirely.
891             //
892 
893             DebugPrint((1, "(%p)\tPreviously sent power irp\n", Irp));
894 
895             if (context->QueueLocked) {
896 
897                 DebugPrint((1, "(%p)\tUnlocking queue\n", Irp));
898 
899                 context->Srb.Length = sizeof(SCSI_REQUEST_BLOCK);
900 
901                 context->Srb.SrbStatus = context->Srb.ScsiStatus = 0;
902                 context->Srb.DataTransferLength = 0;
903 
904                 context->Srb.Function = SRB_FUNCTION_UNLOCK_QUEUE;
905                 context->Srb.SrbFlags = SRB_FLAGS_BYPASS_LOCKED_QUEUE;
906                 nextStack->Parameters.Scsi.Srb = &(context->Srb);
907                 nextStack->MajorFunction = IRP_MJ_SCSI;
908 
909                 IoSetCompletionRoutine(Irp,
910                                        ClasspPowerDownCompletion,
911                                        context,
912                                        TRUE,
913                                        TRUE,
914                                        TRUE);
915 
916                 status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
917                 DebugPrint((1, "(%p)\tIoCallDriver returned %lx\n",
918                             Irp,
919                             status));
920                 break;
921             }
922 
923         }
924 
925         case PowerDownDeviceUnlocked2: {
926 
927             //
928             // This is the end of the dance.  Free the srb and complete the
929             // request finally.  We're ignoring possible intermediate
930             // error conditions ....
931             //
932 
933             if (context->QueueLocked == FALSE) {
934                 DebugPrint((1, "(%p)\tFall through (queue not locked)\n", Irp));
935             } else {
936                 DebugPrint((1, "(%p)\tPreviously unlocked queue\n", Irp));
937                 ASSERT(NT_SUCCESS(Irp->IoStatus.Status));
938                 ASSERT(context->Srb.SrbStatus == SRB_STATUS_SUCCESS);
939             }
940 
941             DebugPrint((1, "(%p)\tFreeing srb and completing\n", Irp));
942             context->InUse = FALSE;
943             status = context->FinalStatus; // allow failure to propagate
944             context = NULL;
945 
946             if(Irp->PendingReturned) {
947                 IoMarkIrpPending(Irp);
948             }
949 
950             Irp->IoStatus.Status = status;
951             Irp->IoStatus.Information = 0;
952 
953             if (NT_SUCCESS(status)) {
954 
955                 //
956                 // Set the new power state
957                 //
958 
959                 fdoExtension->DevicePowerState =
960                     currentStack->Parameters.Power.State.DeviceState;
961 
962             }
963 
964 
965             DebugPrint((1, "(%p)\tStarting next power irp\n", Irp));
966 
967             ClassReleaseRemoveLock(DeviceObject, Irp);
968             PoStartNextPowerIrp(Irp);
969             fdoExtension->PowerDownInProgress = FALSE;
970 
971             return status;
972         }
973 
974         case PowerDownDeviceInitial2: {
975             NT_ASSERT(context->PowerChangeState.PowerDown2 != PowerDownDeviceInitial2);
976             break;
977         }
978     }
979 
980     return STATUS_MORE_PROCESSING_REQUIRED;
981 } // end ClasspPowerDownCompletion()
982 
983 /*++////////////////////////////////////////////////////////////////////////////
984 
985 ClasspPowerHandler()
986 
987 Routine Description:
988 
989     This routine reduces the number of useless spinups and spindown requests
990     sent to a given device by ignoring transitions to power states we are
991     currently in.
992 
993     ISSUE-2000/02/20-henrygab - by ignoring spin-up requests, we may be
994           allowing the drive
995 
996 Arguments:
997 
998     DeviceObject - the device object which is transitioning power states
999     Irp - the power irp
1000     Options - a set of flags indicating what the device handles
1001 
1002 Return Value:
1003 
1004 --*/
1005 NTSTATUS
1006 NTAPI
1007 ClasspPowerHandler(
1008     IN PDEVICE_OBJECT DeviceObject,
1009     IN PIRP Irp,
1010     IN CLASS_POWER_OPTIONS Options  // ISSUE-2000/02/20-henrygab - pass pointer, not whole struct
1011     )
1012 {
1013     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
1014     PDEVICE_OBJECT lowerDevice = commonExtension->LowerDeviceObject;
1015     PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
1016     PIO_STACK_LOCATION nextIrpStack;
1017     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
1018     PCLASS_POWER_CONTEXT context;
1019 
1020     if (!commonExtension->IsFdo) {
1021 
1022         //
1023         // certain assumptions are made here,
1024         // particularly: having the fdoExtension
1025         //
1026 
1027         DebugPrint((0, "ClasspPowerHandler: Called for PDO %p???\n",
1028                     DeviceObject));
1029         ASSERT(!"PDO using ClasspPowerHandler");
1030         return STATUS_NOT_SUPPORTED;
1031     }
1032 
1033     DebugPrint((1, "ClasspPowerHandler: Power irp %p to %s %p\n",
1034                 Irp, (commonExtension->IsFdo ? "fdo" : "pdo"), DeviceObject));
1035 
1036     switch(irpStack->MinorFunction) {
1037 
1038         case IRP_MN_SET_POWER: {
1039             PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
1040 
1041             DebugPrint((1, "(%p)\tIRP_MN_SET_POWER\n", Irp));
1042 
1043             DebugPrint((1, "(%p)\tSetting %s state to %d\n",
1044                         Irp,
1045                         (irpStack->Parameters.Power.Type == SystemPowerState ?
1046                             "System" : "Device"),
1047                         irpStack->Parameters.Power.State.SystemState));
1048 
1049                 switch (irpStack->Parameters.Power.ShutdownType){
1050 
1051                     case PowerActionSleep:
1052                     case PowerActionHibernate:
1053                         if (fdoData->HotplugInfo.MediaRemovable || fdoData->HotplugInfo.MediaHotplug){
1054                             /*
1055                              *  We are suspending and this drive is either hot-pluggable
1056                              *  or contains removeable media.
1057                              *  Set the media dirty bit, since the media may change while
1058                              *  we are suspended.
1059                              */
1060                             SET_FLAG(DeviceObject->Flags, DO_VERIFY_VOLUME);
1061                         }
1062                         break;
1063                     default:
1064                         break;
1065                 }
1066 
1067             break;
1068         }
1069 
1070         default: {
1071 
1072             DebugPrint((1, "(%p)\tIrp minor code = %#x\n",
1073                         Irp, irpStack->MinorFunction));
1074             break;
1075         }
1076     }
1077 
1078     if (irpStack->Parameters.Power.Type != DevicePowerState ||
1079         irpStack->MinorFunction != IRP_MN_SET_POWER) {
1080 
1081         DebugPrint((1, "(%p)\tSending to lower device\n", Irp));
1082 
1083         goto ClasspPowerHandlerCleanup;
1084 
1085     }
1086 
1087     nextIrpStack = IoGetNextIrpStackLocation(Irp);
1088 
1089     //
1090     // already in exact same state, don't work to transition to it.
1091     //
1092 
1093     if(irpStack->Parameters.Power.State.DeviceState ==
1094        fdoExtension->DevicePowerState) {
1095 
1096         DebugPrint((1, "(%p)\tAlready in device state %x\n",
1097                     Irp, fdoExtension->DevicePowerState));
1098         goto ClasspPowerHandlerCleanup;
1099 
1100     }
1101 
1102     //
1103     // or powering down from non-d0 state (device already stopped)
1104     // NOTE -- we're not sure whether this case can exist or not (the
1105     // power system may never send this sort of request) but it's trivial
1106     // to deal with.
1107     //
1108 
1109     if ((irpStack->Parameters.Power.State.DeviceState != PowerDeviceD0) &&
1110         (fdoExtension->DevicePowerState != PowerDeviceD0)) {
1111         DebugPrint((1, "(%p)\tAlready powered down to %x???\n",
1112                     Irp, fdoExtension->DevicePowerState));
1113         fdoExtension->DevicePowerState =
1114             irpStack->Parameters.Power.State.DeviceState;
1115         goto ClasspPowerHandlerCleanup;
1116     }
1117 
1118     //
1119     // or going into a hibernation state when we're in the hibernation path.
1120     // If the device is spinning then we should leave it spinning - if it's not
1121     // then the dump driver will start it up for us.
1122     //
1123 
1124     if((irpStack->Parameters.Power.State.DeviceState == PowerDeviceD3) &&
1125        (irpStack->Parameters.Power.ShutdownType == PowerActionHibernate) &&
1126        (commonExtension->HibernationPathCount != 0)) {
1127 
1128         DebugPrint((1, "(%p)\tdoing nothing for hibernation request for "
1129                        "state %x???\n",
1130                     Irp, fdoExtension->DevicePowerState));
1131         fdoExtension->DevicePowerState =
1132             irpStack->Parameters.Power.State.DeviceState;
1133         goto ClasspPowerHandlerCleanup;
1134     }
1135     //
1136     // or when not handling powering up and are powering up
1137     //
1138 
1139     if ((!Options.HandleSpinUp) &&
1140         (irpStack->Parameters.Power.State.DeviceState == PowerDeviceD0)) {
1141 
1142         DebugPrint((2, "(%p)\tNot handling spinup to state %x\n",
1143                     Irp, fdoExtension->DevicePowerState));
1144         fdoExtension->DevicePowerState =
1145             irpStack->Parameters.Power.State.DeviceState;
1146         goto ClasspPowerHandlerCleanup;
1147 
1148     }
1149 
1150     //
1151     // or when not handling powering down and are powering down
1152     //
1153 
1154     if ((!Options.HandleSpinDown) &&
1155         (irpStack->Parameters.Power.State.DeviceState != PowerDeviceD0)) {
1156 
1157         DebugPrint((2, "(%p)\tNot handling spindown to state %x\n",
1158                     Irp, fdoExtension->DevicePowerState));
1159         fdoExtension->DevicePowerState =
1160             irpStack->Parameters.Power.State.DeviceState;
1161         goto ClasspPowerHandlerCleanup;
1162 
1163     }
1164 
1165     context = &(fdoExtension->PowerContext);
1166 
1167 #if DBG
1168     //
1169     // Mark the context as in use.  We should be synchronizing this but
1170     // since it's just for debugging purposes we don't worry too much.
1171     //
1172 
1173     ASSERT(context->InUse == FALSE);
1174 #endif
1175 
1176     RtlZeroMemory(context, sizeof(CLASS_POWER_CONTEXT));
1177     context->InUse = TRUE;
1178 
1179     nextIrpStack->Parameters.Scsi.Srb = &(context->Srb);
1180     nextIrpStack->MajorFunction = IRP_MJ_SCSI;
1181 
1182     context->FinalStatus = STATUS_SUCCESS;
1183 
1184     context->Srb.Length = sizeof(SCSI_REQUEST_BLOCK);
1185     context->Srb.OriginalRequest = Irp;
1186     context->Srb.SrbFlags |= SRB_FLAGS_BYPASS_LOCKED_QUEUE
1187                           |  SRB_FLAGS_NO_QUEUE_FREEZE;
1188     context->Srb.Function = SRB_FUNCTION_LOCK_QUEUE;
1189 
1190     context->Srb.SenseInfoBuffer =
1191         commonExtension->PartitionZeroExtension->SenseData;
1192     context->Srb.SenseInfoBufferLength = SENSE_BUFFER_SIZE;
1193     context->RetryCount = MAXIMUM_RETRIES;
1194 
1195     context->Options = Options;
1196     context->DeviceObject = DeviceObject;
1197     context->Irp = Irp;
1198 
1199     if(irpStack->Parameters.Power.State.DeviceState == PowerDeviceD0) {
1200 
1201         ASSERT(Options.HandleSpinUp);
1202 
1203         DebugPrint((2, "(%p)\tpower up - locking queue\n", Irp));
1204 
1205         //
1206         // We need to issue a queue lock request so that we
1207         // can spin the drive back up after the power is restored
1208         // but before any requests are processed.
1209         //
1210 
1211         context->Options.PowerDown = FALSE;
1212         context->PowerChangeState.PowerUp = PowerUpDeviceInitial;
1213         context->CompletionRoutine = ClasspPowerUpCompletion;
1214 
1215     } else {
1216 
1217         ASSERT(Options.HandleSpinDown);
1218 
1219         fdoExtension->PowerDownInProgress = TRUE;
1220 
1221         DebugPrint((2, "(%p)\tPowering down - locking queue\n", Irp));
1222 
1223         PoSetPowerState(DeviceObject,
1224                         irpStack->Parameters.Power.Type,
1225                         irpStack->Parameters.Power.State);
1226 
1227         context->Options.PowerDown = TRUE;
1228         context->PowerChangeState.PowerDown2 = PowerDownDeviceInitial2;
1229         context->CompletionRoutine = ClasspPowerDownCompletion;
1230 
1231     }
1232 
1233     //
1234     // we are not dealing with port-allocated sense in these routines.
1235     //
1236 
1237     ASSERT(!TEST_FLAG(context->Srb.SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
1238     ASSERT(!TEST_FLAG(context->Srb.SrbFlags, SRB_FLAGS_PORT_DRIVER_ALLOCSENSE));
1239 
1240     //
1241     // we are always returning STATUS_PENDING, so we need to always
1242     // set the irp as pending.
1243     //
1244 
1245     IoMarkIrpPending(Irp);
1246 
1247     if(Options.LockQueue) {
1248 
1249         //
1250         // Send the lock irp down.
1251         //
1252 
1253         IoSetCompletionRoutine(Irp,
1254                                context->CompletionRoutine,
1255                                context,
1256                                TRUE,
1257                                TRUE,
1258                                TRUE);
1259 
1260         IoCallDriver(lowerDevice, Irp);
1261 
1262     } else {
1263 
1264         //
1265         // Call the completion routine directly.  It won't care what the
1266         // status of the "lock" was - it will just go and do the next
1267         // step of the operation.
1268         //
1269 
1270         context->CompletionRoutine(DeviceObject, Irp, context);
1271     }
1272 
1273     return STATUS_PENDING;
1274 
1275 ClasspPowerHandlerCleanup:
1276 
1277     ClassReleaseRemoveLock(DeviceObject, Irp);
1278 
1279     DebugPrint((1, "(%p)\tStarting next power irp\n", Irp));
1280     IoCopyCurrentIrpStackLocationToNext(Irp);
1281     IoSetCompletionRoutine(Irp,
1282                            ClasspStartNextPowerIrpCompletion,
1283                            NULL,
1284                            TRUE,
1285                            TRUE,
1286                            TRUE);
1287     return PoCallDriver(lowerDevice, Irp);
1288 } // end ClasspPowerHandler()
1289 
1290 /*++////////////////////////////////////////////////////////////////////////////
1291 
1292 ClassMinimalPowerHandler()
1293 
1294 Routine Description:
1295 
1296     This routine is the minimum power handler for a storage driver.  It does
1297     the least amount of work possible.
1298 
1299 --*/
1300 NTSTATUS
1301 NTAPI
1302 ClassMinimalPowerHandler(
1303     IN PDEVICE_OBJECT DeviceObject,
1304     IN PIRP Irp
1305     )
1306 {
1307     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
1308     PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
1309     NTSTATUS status;
1310 
1311     ClassReleaseRemoveLock(DeviceObject, Irp);
1312     PoStartNextPowerIrp(Irp);
1313 
1314     if(commonExtension->IsFdo) {
1315 
1316         if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) {
1317 
1318             PFUNCTIONAL_DEVICE_EXTENSION fdoExtension =
1319                 DeviceObject->DeviceExtension;
1320 
1321             //
1322             // Check if the system is going to hibernate or standby.
1323             //
1324             if (irpStack->MinorFunction == IRP_MN_SET_POWER){
1325                 PVPB vpb;
1326 
1327                 switch (irpStack->Parameters.Power.ShutdownType){
1328 
1329                     case PowerActionSleep:
1330                     case PowerActionHibernate:
1331                         //
1332                         // If the volume is mounted, set the verify bit so that
1333                         // the filesystem will be forced re-read the media
1334                         // after coming out of hibernation or standby.
1335                         //
1336                         vpb = ClassGetVpb(fdoExtension->DeviceObject);
1337                         if (vpb && (vpb->Flags & VPB_MOUNTED)){
1338                             SET_FLAG(fdoExtension->DeviceObject->Flags, DO_VERIFY_VOLUME);
1339                         }
1340                         break;
1341                     default:
1342                         break;
1343                 }
1344             }
1345         }
1346 
1347         IoCopyCurrentIrpStackLocationToNext(Irp);
1348         return PoCallDriver(commonExtension->LowerDeviceObject, Irp);
1349 
1350     } else {
1351 
1352         if (irpStack->MinorFunction != IRP_MN_SET_POWER &&
1353             irpStack->MinorFunction != IRP_MN_QUERY_POWER) {
1354 
1355             NOTHING;
1356 
1357         } else {
1358 
1359             Irp->IoStatus.Status = STATUS_SUCCESS;
1360             Irp->IoStatus.Information = 0;
1361 
1362         }
1363         status = Irp->IoStatus.Status;
1364 
1365         ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
1366         return status;
1367     }
1368 } // end ClassMinimalPowerHandler()
1369 
1370 /*++////////////////////////////////////////////////////////////////////////////
1371 
1372 ClassSpinDownPowerHandler()
1373 
1374 Routine Description:
1375 
1376     This routine is a callback for disks and other things which require both
1377     a start and a stop to be sent to the device.  (actually the starts are
1378     almost always optional, since most device power themselves on to process
1379     commands, but i digress).
1380 
1381     Determines proper use of spinup, spindown, and queue locking based upon
1382     ScanForSpecialFlags in the FdoExtension.  This is the most common power
1383     handler passed into classpnp.sys
1384 
1385 Arguments:
1386 
1387     DeviceObject - Supplies the functional device object
1388 
1389     Irp - Supplies the request to be retried.
1390 
1391 Return Value:
1392 
1393     None
1394 
1395 --*/
1396 NTSTATUS
1397 NTAPI
1398 ClassSpinDownPowerHandler(
1399     IN PDEVICE_OBJECT DeviceObject,
1400     IN PIRP Irp
1401     )
1402 {
1403     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
1404     CLASS_POWER_OPTIONS options;
1405 
1406     fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
1407 
1408     //
1409     // this will set all options to FALSE
1410     //
1411 
1412     RtlZeroMemory(&options, sizeof(CLASS_POWER_OPTIONS));
1413 
1414     //
1415     // check the flags to see what options we need to worry about
1416     //
1417 
1418     if (!TEST_FLAG(fdoExtension->ScanForSpecialFlags,
1419                   CLASS_SPECIAL_DISABLE_SPIN_DOWN)) {
1420         options.HandleSpinDown = TRUE;
1421     }
1422 
1423     if (!TEST_FLAG(fdoExtension->ScanForSpecialFlags,
1424                   CLASS_SPECIAL_DISABLE_SPIN_UP)) {
1425         options.HandleSpinUp = TRUE;
1426     }
1427 
1428     if (!TEST_FLAG(fdoExtension->ScanForSpecialFlags,
1429                   CLASS_SPECIAL_NO_QUEUE_LOCK)) {
1430         options.LockQueue = TRUE;
1431     }
1432 
1433     DebugPrint((3, "ClasspPowerHandler: Devobj %p\n"
1434                 "\t%shandling spin down\n"
1435                 "\t%shandling spin up\n"
1436                 "\t%slocking queue\n",
1437                 DeviceObject,
1438                 (options.HandleSpinDown ? "" : "not "),
1439                 (options.HandleSpinUp   ? "" : "not "),
1440                 (options.LockQueue      ? "" : "not ")
1441                 ));
1442 
1443     //
1444     // do all the dirty work
1445     //
1446 
1447     return ClasspPowerHandler(DeviceObject, Irp, options);
1448 } // end ClassSpinDownPowerHandler()
1449 
1450 /*++////////////////////////////////////////////////////////////////////////////
1451 
1452 ClassStopUnitPowerHandler()
1453 
1454 Routine Description:
1455 
1456     This routine is an outdated call.  To achieve equivalent functionality,
1457     the driver should set the following flags in ScanForSpecialFlags in the
1458     FdoExtension:
1459 
1460         CLASS_SPECIAL_DISABLE_SPIN_UP
1461         CLASS_SPECIAL_NO_QUEUE_LOCK
1462 
1463 --*/
1464 NTSTATUS
1465 NTAPI
1466 ClassStopUnitPowerHandler(
1467     IN PDEVICE_OBJECT DeviceObject,
1468     IN PIRP Irp
1469     )
1470 {
1471     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
1472 
1473     DebugPrint((0, "ClassStopUnitPowerHandler - Devobj %p using outdated call\n"
1474                 "Drivers should set the following flags in ScanForSpecialFlags "
1475                 " in the FDO extension:\n"
1476                 "\tCLASS_SPECIAL_DISABLE_SPIN_UP\n"
1477                 "\tCLASS_SPECIAL_NO_QUEUE_LOCK\n"
1478                 "This will provide equivalent functionality if the power "
1479                 "routine is then set to ClassSpinDownPowerHandler\n\n",
1480                 DeviceObject));
1481 
1482     fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
1483 
1484     SET_FLAG(fdoExtension->ScanForSpecialFlags,
1485              CLASS_SPECIAL_DISABLE_SPIN_UP);
1486     SET_FLAG(fdoExtension->ScanForSpecialFlags,
1487              CLASS_SPECIAL_NO_QUEUE_LOCK);
1488 
1489     return ClassSpinDownPowerHandler(DeviceObject, Irp);
1490 } // end ClassStopUnitPowerHandler()
1491 
1492 /*++////////////////////////////////////////////////////////////////////////////
1493 
1494 RetryPowerRequest()
1495 
1496 Routine Description:
1497 
1498     This routine reinitializes the necessary fields, and sends the request
1499     to the lower driver.
1500 
1501 Arguments:
1502 
1503     DeviceObject - Supplies the device object associated with this request.
1504 
1505     Irp - Supplies the request to be retried.
1506 
1507     Context - Supplies a pointer to the power up context for this request.
1508 
1509 Return Value:
1510 
1511     None
1512 
1513 --*/
1514 VOID
1515 NTAPI
1516 RetryPowerRequest(
1517     PDEVICE_OBJECT DeviceObject,
1518     PIRP Irp,
1519     PCLASS_POWER_CONTEXT Context
1520     )
1521 {
1522     PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp);
1523     PSCSI_REQUEST_BLOCK srb = &(Context->Srb);
1524     LARGE_INTEGER dueTime;
1525 
1526     DebugPrint((1, "(%p)\tDelaying retry by queueing DPC\n", Irp));
1527 
1528     ASSERT(Context->Irp == Irp);
1529     ASSERT(Context->DeviceObject == DeviceObject);
1530     ASSERT(!TEST_FLAG(Context->Srb.SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
1531     ASSERT(!TEST_FLAG(Context->Srb.SrbFlags, SRB_FLAGS_PORT_DRIVER_ALLOCSENSE));
1532 
1533     //
1534     // reset the retry interval
1535     //
1536 
1537     Context->RetryInterval = 0;
1538 
1539     //
1540     // Reset byte count of transfer in SRB Extension.
1541     //
1542 
1543     srb->DataTransferLength = 0;
1544 
1545     //
1546     // Zero SRB statuses.
1547     //
1548 
1549     srb->SrbStatus = srb->ScsiStatus = 0;
1550 
1551     //
1552     // Set up major SCSI function.
1553     //
1554 
1555     nextIrpStack->MajorFunction = IRP_MJ_SCSI;
1556 
1557     //
1558     // Save SRB address in next stack for port driver.
1559     //
1560 
1561     nextIrpStack->Parameters.Scsi.Srb = srb;
1562 
1563     //
1564     // Set the completion routine up again.
1565     //
1566 
1567     IoSetCompletionRoutine(Irp, Context->CompletionRoutine, Context,
1568                            TRUE, TRUE, TRUE);
1569 
1570 
1571     if (Context->RetryInterval == 0) {
1572 
1573         DebugPrint((2, "(%p)\tDelaying minimum time (.2 sec)\n", Irp));
1574         dueTime.QuadPart = (LONGLONG)1000000 * 2;
1575 
1576     } else {
1577 
1578         DebugPrint((2, "(%p)\tDelaying %x seconds\n",
1579                     Irp, Context->RetryInterval));
1580         dueTime.QuadPart = (LONGLONG)1000000 * 10 * Context->RetryInterval;
1581 
1582     }
1583 
1584     ClassRetryRequest(DeviceObject, Irp, dueTime);
1585 
1586     return;
1587 
1588 } // end RetryRequest()
1589 
1590 /*++////////////////////////////////////////////////////////////////////////////
1591 
1592 ClasspStartNextPowerIrpCompletion()
1593 
1594 Routine Description:
1595 
1596     This routine guarantees that the next power irp (power up or down) is not
1597     sent until the previous one has fully completed.
1598 
1599 --*/
1600 NTSTATUS
1601 NTAPI
1602 ClasspStartNextPowerIrpCompletion(
1603     IN PDEVICE_OBJECT DeviceObject,
1604     IN PIRP Irp,
1605     IN PVOID Context
1606     )
1607 {
1608     if(Irp->PendingReturned) {
1609         IoMarkIrpPending(Irp);
1610     }
1611 
1612     PoStartNextPowerIrp(Irp);
1613     return STATUS_SUCCESS;
1614 } // end ClasspStartNextPowerIrpCompletion()
1615