xref: /reactos/drivers/storage/class/disk/diskwmi.c (revision 3088717b)
1 /*++
2 
3 Copyright (C) Microsoft Corporation, 1991 - 2010
4 
5 Module Name:
6 
7     diskwmi.c
8 
9 Abstract:
10 
11     SCSI disk class driver - WMI support routines
12 
13 Environment:
14 
15     kernel mode only
16 
17 Notes:
18 
19 Revision History:
20 
21 --*/
22 
23 #include "disk.h"
24 
25 #ifdef DEBUG_USE_WPP
26 #include "diskwmi.tmh"
27 #endif
28 
29 NTSTATUS
30 DiskSendFailurePredictIoctl(
31     PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
32     PSTORAGE_PREDICT_FAILURE checkFailure
33     );
34 
35 NTSTATUS
36 DiskGetIdentifyInfo(
37     PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
38     PBOOLEAN SupportSmart
39     );
40 
41 NTSTATUS
42 DiskDetectFailurePrediction(
43     PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
44     PFAILURE_PREDICTION_METHOD FailurePredictCapability,
45     BOOLEAN ScsiAddressAvailable
46     );
47 
48 NTSTATUS
49 DiskReadFailurePredictThresholds(
50     PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
51     PSTORAGE_FAILURE_PREDICT_THRESHOLDS DiskSmartThresholds
52     );
53 
54 NTSTATUS
55 DiskReadSmartLog(
56     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
57     IN UCHAR SectorCount,
58     IN UCHAR LogAddress,
59     OUT PUCHAR Buffer
60     );
61 
62 NTSTATUS
63 DiskWriteSmartLog(
64     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
65     IN UCHAR SectorCount,
66     IN UCHAR LogAddress,
67     IN PUCHAR Buffer
68     );
69 
70 IO_WORKITEM_ROUTINE DiskReregWorker;
71 
72 IO_COMPLETION_ROUTINE DiskInfoExceptionComplete;
73 
74 //
75 // WMI reregistration globals
76 //
77 // Since it will take too long to do a mode sense on some drive, we
78 // need a good way to effect the mode sense for the info exceptions
79 // mode page so that we can determine if SMART is supported and enabled
80 // for the drive. So the strategy is to do an asynchronous mode sense
81 // when the device starts and then look at the info exceptions mode
82 // page within the completion routine. Now within the completion
83 // routine we cannot call IoWMIRegistrationControl since we are at DPC
84 // level, so we create a stack of device objects that will be processed
85 // by a single work item that is fired off only when the stack
86 // transitions from empty to non empty.
87 //
88 SINGLE_LIST_ENTRY DiskReregHead;
89 KSPIN_LOCK DiskReregSpinlock;
90 LONG DiskReregWorkItems;
91 
92 GUIDREGINFO DiskWmiFdoGuidList[] =
93 {
94     {
95         WMI_DISK_GEOMETRY_GUID,
96         1,
97         0
98     },
99 
100     {
101         WMI_STORAGE_FAILURE_PREDICT_STATUS_GUID,
102         1,
103         WMIREG_FLAG_EXPENSIVE
104     },
105 
106     {
107         WMI_STORAGE_FAILURE_PREDICT_DATA_GUID,
108         1,
109         WMIREG_FLAG_EXPENSIVE
110     },
111 
112     {
113         WMI_STORAGE_FAILURE_PREDICT_FUNCTION_GUID,
114         1,
115         WMIREG_FLAG_EXPENSIVE
116     },
117 
118     {
119         WMI_STORAGE_PREDICT_FAILURE_EVENT_GUID,
120         1,
121         WMIREG_FLAG_EVENT_ONLY_GUID
122     },
123 
124     {
125         WMI_STORAGE_FAILURE_PREDICT_THRESHOLDS_GUID,
126         1,
127         WMIREG_FLAG_EXPENSIVE
128     },
129 
130     {
131         WMI_STORAGE_SCSI_INFO_EXCEPTIONS_GUID,
132         1,
133         0
134     }
135 };
136 
137 
138 GUID DiskPredictFailureEventGuid = WMI_STORAGE_PREDICT_FAILURE_EVENT_GUID;
139 
140 #define DiskGeometryGuid           0
141 #define SmartStatusGuid            1
142 #define SmartDataGuid              2
143 #define SmartPerformFunction       3
144     #define AllowDisallowPerformanceHit                 1
145     #define EnableDisableHardwareFailurePrediction      2
146     #define EnableDisableFailurePredictionPolling       3
147     #define GetFailurePredictionCapability              4
148     #define EnableOfflineDiags                          5
149 
150 #define SmartEventGuid             4
151 #define SmartThresholdsGuid        5
152 #define ScsiInfoExceptionsGuid     6
153 
154 #ifdef ALLOC_PRAGMA
155 
156 #pragma alloc_text(PAGE, DiskWmiFunctionControl)
157 #pragma alloc_text(PAGE, DiskFdoQueryWmiRegInfo)
158 #pragma alloc_text(PAGE, DiskFdoQueryWmiDataBlock)
159 #pragma alloc_text(PAGE, DiskFdoSetWmiDataBlock)
160 #pragma alloc_text(PAGE, DiskFdoSetWmiDataItem)
161 #pragma alloc_text(PAGE, DiskFdoExecuteWmiMethod)
162 
163 #pragma alloc_text(PAGE, DiskDetectFailurePrediction)
164 #pragma alloc_text(PAGE, DiskEnableDisableFailurePrediction)
165 #pragma alloc_text(PAGE, DiskEnableDisableFailurePredictPolling)
166 #pragma alloc_text(PAGE, DiskReadFailurePredictStatus)
167 #pragma alloc_text(PAGE, DiskReadFailurePredictData)
168 #pragma alloc_text(PAGE, DiskReadFailurePredictThresholds)
169 #pragma alloc_text(PAGE, DiskGetIdentifyInfo)
170 #pragma alloc_text(PAGE, DiskReadSmartLog)
171 #pragma alloc_text(PAGE, DiskWriteSmartLog)
172 #pragma alloc_text(PAGE, DiskPerformSmartCommand)
173 #pragma alloc_text(PAGE, DiskSendFailurePredictIoctl)
174 #pragma alloc_text(PAGE, DiskReregWorker)
175 #pragma alloc_text(PAGE, DiskInitializeReregistration)
176 
177 #if (NTDDI_VERSION >= NTDDI_WINBLUE)
178 #pragma alloc_text(PAGE, DiskGetModePage)
179 #pragma alloc_text(PAGE, DiskEnableInfoExceptions)
180 #endif // (NTDDI_VERSION >= NTDDI_WINBLUE)
181 
182 #endif
183 
184 
185 //
186 // Note:
187 // Some port drivers assume that the SENDCMDINPARAMS structure will always be atleast
188 // sizeof(SENDCMDINPARAMS). So do not adjust for the [pBuffer] if it isn't being used
189 //
190 
191 //
192 // SMART/IDE specific routines
193 //
194 
195 //
196 // Read SMART data attributes.
197 // SrbControl should be : sizeof(SRB_IO_CONTROL) + MAX[ sizeof(SENDCMDINPARAMS), sizeof(SENDCMDOUTPARAMS) - 1 + READ_ATTRIBUTE_BUFFER_SIZE ]
198 // Attribute data returned at &SendCmdOutParams->bBuffer[0]
199 //
200 #define DiskReadSmartData(FdoExtension,                                 \
201                           SrbControl,                                   \
202                           BufferSize)                                   \
203     DiskPerformSmartCommand(FdoExtension,                               \
204                             IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS,     \
205                             SMART_CMD,                                  \
206                             READ_ATTRIBUTES,                            \
207                             0,                                          \
208                             0,                                          \
209                             (SrbControl),                               \
210                             (BufferSize))
211 
212 
213 //
214 // Read SMART data thresholds.
215 // SrbControl should be : sizeof(SRB_IO_CONTROL) + MAX[ sizeof(SENDCMDINPARAMS), sizeof(SENDCMDOUTPARAMS) - 1 + READ_THRESHOLD_BUFFER_SIZE ]
216 // Attribute data returned at &SendCmdOutParams->bBuffer[0]
217 //
218 #define DiskReadSmartThresholds(FdoExtension,                           \
219                                 SrbControl,                             \
220                                 BufferSize)                             \
221     DiskPerformSmartCommand(FdoExtension,                               \
222                             IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS,  \
223                             SMART_CMD,                                  \
224                             READ_THRESHOLDS,                            \
225                             0,                                          \
226                             0,                                          \
227                             (SrbControl),                               \
228                             (BufferSize))
229 
230 
231 //
232 // Read SMART status
233 // SrbControl should be : sizeof(SRB_IO_CONTROL) + MAX[ sizeof(SENDCMDINPARAMS), sizeof(SENDCMDOUTPARAMS) - 1 + sizeof(IDEREGS) ]
234 // Failure predicted if SendCmdOutParams->bBuffer[3] == 0xf4 and SendCmdOutParams->bBuffer[4] == 0x2c
235 //
236 #define DiskReadSmartStatus(FdoExtension,                               \
237                             SrbControl,                                 \
238                             BufferSize)                                 \
239     DiskPerformSmartCommand(FdoExtension,                               \
240                             IOCTL_SCSI_MINIPORT_RETURN_STATUS,          \
241                             SMART_CMD,                                  \
242                             RETURN_SMART_STATUS,                        \
243                             0,                                          \
244                             0,                                          \
245                             (SrbControl),                               \
246                             (BufferSize))
247 
248 
249 //
250 // Read disks IDENTIFY data
251 // SrbControl should be : sizeof(SRB_IO_CONTROL) + MAX[ sizeof(SENDCMDINPARAMS), sizeof(SENDCMDOUTPARAMS) - 1 + IDENTIFY_BUFFER_SIZE ]
252 // Identify data returned at &SendCmdOutParams->bBuffer[0]
253 //
254 #define DiskGetIdentifyData(FdoExtension,                               \
255                             SrbControl,                                 \
256                             BufferSize)                                 \
257     DiskPerformSmartCommand(FdoExtension,                               \
258                             IOCTL_SCSI_MINIPORT_IDENTIFY,               \
259                             ID_CMD,                                     \
260                             0,                                          \
261                             0,                                          \
262                             0,                                          \
263                             (SrbControl),                               \
264                             (BufferSize))
265 
266 
267 //
268 // Enable SMART
269 //
270 static NTSTATUS
DiskEnableSmart(PFUNCTIONAL_DEVICE_EXTENSION FdoExtension)271 DiskEnableSmart(
272     PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
273     )
274 {
275     UCHAR srbControl[sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)] = {0};
276     ULONG bufferSize = sizeof(srbControl);
277 
278     return DiskPerformSmartCommand(FdoExtension,
279                                    IOCTL_SCSI_MINIPORT_ENABLE_SMART,
280                                    SMART_CMD,
281                                    ENABLE_SMART,
282                                    0,
283                                    0,
284                                    (PSRB_IO_CONTROL)srbControl,
285                                    &bufferSize);
286 }
287 
288 
289 //
290 // Disable SMART
291 //
292 static NTSTATUS
DiskDisableSmart(PFUNCTIONAL_DEVICE_EXTENSION FdoExtension)293 DiskDisableSmart(
294     PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
295     )
296 {
297     UCHAR srbControl[sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)] = {0};
298     ULONG bufferSize = sizeof(srbControl);
299 
300     return DiskPerformSmartCommand(FdoExtension,
301                                    IOCTL_SCSI_MINIPORT_DISABLE_SMART,
302                                    SMART_CMD,
303                                    DISABLE_SMART,
304                                    0,
305                                    0,
306                                    (PSRB_IO_CONTROL)srbControl,
307                                    &bufferSize);
308 }
309 
310 #ifndef __REACTOS__ // functions are not used
311 //
312 // Enable Attribute Autosave
313 //
314 _inline NTSTATUS
DiskEnableSmartAttributeAutosave(PFUNCTIONAL_DEVICE_EXTENSION FdoExtension)315 DiskEnableSmartAttributeAutosave(
316     PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
317     )
318 {
319     UCHAR srbControl[sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)] = {0};
320     ULONG bufferSize = sizeof(srbControl);
321 
322     return DiskPerformSmartCommand(FdoExtension,
323                                    IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE,
324                                    SMART_CMD,
325                                    ENABLE_DISABLE_AUTOSAVE,
326                                    0xf1,
327                                    0,
328                                    (PSRB_IO_CONTROL)srbControl,
329                                    &bufferSize);
330 }
331 
332 
333 //
334 // Disable Attribute Autosave
335 //
336 _inline NTSTATUS
DiskDisableSmartAttributeAutosave(PFUNCTIONAL_DEVICE_EXTENSION FdoExtension)337 DiskDisableSmartAttributeAutosave(
338     PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
339     )
340 {
341     UCHAR srbControl[sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)] = {0};
342     ULONG bufferSize = sizeof(srbControl);
343 
344     return DiskPerformSmartCommand(FdoExtension,
345                                    IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE,
346                                    SMART_CMD,
347                                    ENABLE_DISABLE_AUTOSAVE,
348                                    0x00,
349                                    0,
350                                    (PSRB_IO_CONTROL)srbControl,
351                                    &bufferSize);
352 }
353 #endif
354 
355 //
356 // Initialize execution of SMART online diagnostics
357 //
358 static NTSTATUS
DiskExecuteSmartDiagnostics(PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,UCHAR Subcommand)359 DiskExecuteSmartDiagnostics(
360     PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
361     UCHAR Subcommand
362     )
363 {
364     UCHAR srbControl[sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)] = {0};
365     ULONG bufferSize = sizeof(srbControl);
366 
367     return DiskPerformSmartCommand(FdoExtension,
368                                    IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS,
369                                    SMART_CMD,
370                                    EXECUTE_OFFLINE_DIAGS,
371                                    0,
372                                    Subcommand,
373                                    (PSRB_IO_CONTROL)srbControl,
374                                    &bufferSize);
375 }
376 
377 
378 NTSTATUS
DiskReadSmartLog(IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,IN UCHAR SectorCount,IN UCHAR LogAddress,OUT PUCHAR Buffer)379 DiskReadSmartLog(
380     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
381     IN UCHAR SectorCount,
382     IN UCHAR LogAddress,
383     OUT PUCHAR Buffer
384     )
385 {
386     PSRB_IO_CONTROL srbControl;
387     NTSTATUS status;
388     PSENDCMDOUTPARAMS sendCmdOutParams;
389     ULONG logSize, bufferSize;
390 
391     PAGED_CODE();
392 
393     logSize = SectorCount * SMART_LOG_SECTOR_SIZE;
394     bufferSize = sizeof(SRB_IO_CONTROL) +  max( sizeof(SENDCMDINPARAMS), sizeof(SENDCMDOUTPARAMS) - 1 + logSize );
395 
396     srbControl = ExAllocatePoolWithTag(NonPagedPoolNx,
397                                        bufferSize,
398                                        DISK_TAG_SMART);
399 
400     if (srbControl != NULL)
401     {
402         status = DiskPerformSmartCommand(FdoExtension,
403                                          IOCTL_SCSI_MINIPORT_READ_SMART_LOG,
404                                          SMART_CMD,
405                                          SMART_READ_LOG,
406                                          SectorCount,
407                                          LogAddress,
408                                          srbControl,
409                                          &bufferSize);
410 
411         if (NT_SUCCESS(status))
412         {
413             sendCmdOutParams = (PSENDCMDOUTPARAMS)((PUCHAR)srbControl +
414                                                    sizeof(SRB_IO_CONTROL));
415             RtlCopyMemory(Buffer,
416                           &sendCmdOutParams->bBuffer[0],
417                           logSize);
418         }
419 
420         FREE_POOL(srbControl);
421     } else {
422         status = STATUS_INSUFFICIENT_RESOURCES;
423     }
424     return(status);
425 }
426 
427 
428 NTSTATUS
DiskWriteSmartLog(IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,IN UCHAR SectorCount,IN UCHAR LogAddress,IN PUCHAR Buffer)429 DiskWriteSmartLog(
430     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
431     IN UCHAR SectorCount,
432     IN UCHAR LogAddress,
433     IN PUCHAR Buffer
434     )
435 {
436     PSRB_IO_CONTROL srbControl;
437     NTSTATUS status;
438     PSENDCMDINPARAMS sendCmdInParams;
439     ULONG logSize, bufferSize;
440 
441     PAGED_CODE();
442 
443     logSize = SectorCount * SMART_LOG_SECTOR_SIZE;
444     bufferSize = sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS) - 1 +
445                  logSize;
446 
447     srbControl = ExAllocatePoolWithTag(NonPagedPoolNx,
448                                        bufferSize,
449                                        DISK_TAG_SMART);
450 
451     if (srbControl != NULL)
452     {
453         sendCmdInParams = (PSENDCMDINPARAMS)((PUCHAR)srbControl +
454                                                sizeof(SRB_IO_CONTROL));
455         RtlCopyMemory(&sendCmdInParams->bBuffer[0],
456                       Buffer,
457                       logSize);
458         status = DiskPerformSmartCommand(FdoExtension,
459                                          IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG,
460                                          SMART_CMD,
461                                          SMART_WRITE_LOG,
462                                          SectorCount,
463                                          LogAddress,
464                                          srbControl,
465                                          &bufferSize);
466 
467         FREE_POOL(srbControl);
468     } else {
469         status = STATUS_INSUFFICIENT_RESOURCES;
470     }
471     return(status);
472 }
473 
474 
475 NTSTATUS
DiskPerformSmartCommand(IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,IN ULONG SrbControlCode,IN UCHAR Command,IN UCHAR Feature,IN UCHAR SectorCount,IN UCHAR SectorNumber,IN OUT PSRB_IO_CONTROL SrbControl,OUT PULONG BufferSize)476 DiskPerformSmartCommand(
477     IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
478     IN ULONG SrbControlCode,
479     IN UCHAR Command,
480     IN UCHAR Feature,
481     IN UCHAR SectorCount,
482     IN UCHAR SectorNumber,
483     IN OUT PSRB_IO_CONTROL SrbControl,
484     OUT PULONG BufferSize
485     )
486 /*++
487 
488 Routine Description:
489 
490     This routine will perform some SMART command
491 
492 Arguments:
493 
494     FdoExtension is the FDO device extension
495 
496     SrbControlCode is the SRB control code to use for the request
497 
498     Command is the SMART command to be executed. It may be SMART_CMD or
499         ID_CMD.
500 
501     Feature is the value to place in the IDE feature register.
502 
503     SectorCount is the value to place in the IDE SectorCount register
504 
505     SrbControl is the buffer used to build the SRB_IO_CONTROL and pass
506         any input parameters. It also returns the output parameters.
507 
508     *BufferSize on entry has total size of SrbControl and on return has
509         the size used in SrbControl.
510 
511 
512 
513 Return Value:
514 
515     status
516 
517 --*/
518 {
519     PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)FdoExtension;
520     PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
521     PUCHAR buffer;
522     PSENDCMDINPARAMS cmdInParameters;
523     NTSTATUS status;
524     ULONG availableBufferSize;
525     KEVENT event;
526     PIRP irp;
527     IO_STATUS_BLOCK ioStatus = { 0 };
528     SCSI_REQUEST_BLOCK srb = {0};
529     LARGE_INTEGER startingOffset;
530     ULONG length;
531     PIO_STACK_LOCATION irpStack;
532     UCHAR srbExBuffer[CLASS_SRBEX_NO_SRBEX_DATA_BUFFER_SIZE] = {0};
533     PSTORAGE_REQUEST_BLOCK srbEx = (PSTORAGE_REQUEST_BLOCK)srbExBuffer;
534     PSTOR_ADDR_BTL8 storAddrBtl8;
535 
536     PAGED_CODE();
537 
538     //
539     // Point to the 'buffer' portion of the SRB_CONTROL and compute how
540     // much room we have left in the srb control. Abort if the buffer
541     // isn't at least the size of SRB_IO_CONTROL.
542     //
543 
544     buffer = (PUCHAR)SrbControl + sizeof(SRB_IO_CONTROL);
545 
546     cmdInParameters = (PSENDCMDINPARAMS)buffer;
547 
548     if (*BufferSize >= sizeof(SRB_IO_CONTROL)) {
549         availableBufferSize = *BufferSize - sizeof(SRB_IO_CONTROL);
550     } else {
551         return STATUS_BUFFER_TOO_SMALL;
552     }
553 
554 #if DBG
555 
556     //
557     // Ensure control codes and buffer lengths passed are correct
558     //
559     {
560         ULONG controlCode = 0;
561         ULONG lengthNeeded = sizeof(SENDCMDINPARAMS);
562 
563         if (Command == SMART_CMD)
564         {
565             switch (Feature)
566             {
567                 case ENABLE_SMART:
568                 {
569                     controlCode = IOCTL_SCSI_MINIPORT_ENABLE_SMART;
570                     break;
571                 }
572 
573                 case DISABLE_SMART:
574                 {
575                     controlCode = IOCTL_SCSI_MINIPORT_DISABLE_SMART;
576                     break;
577                 }
578 
579                 case RETURN_SMART_STATUS:
580                 {
581                     controlCode = IOCTL_SCSI_MINIPORT_RETURN_STATUS;
582                     lengthNeeded = max( lengthNeeded, sizeof(SENDCMDOUTPARAMS) - 1 + sizeof(IDEREGS) );
583                     break;
584                 }
585 
586                 case ENABLE_DISABLE_AUTOSAVE:
587                 {
588                     controlCode = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE;
589                     break;
590                 }
591 
592                 case SAVE_ATTRIBUTE_VALUES:
593                 {
594                     controlCode = IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES;
595                     break;
596                 }
597 
598 
599                 case EXECUTE_OFFLINE_DIAGS:
600                 {
601                     controlCode = IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS;
602                     break;
603                 }
604 
605                 case READ_ATTRIBUTES:
606                 {
607                     controlCode  = IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS;
608                     lengthNeeded = max( lengthNeeded, sizeof(SENDCMDOUTPARAMS) - 1 + READ_ATTRIBUTE_BUFFER_SIZE );
609                     break;
610                 }
611 
612                 case READ_THRESHOLDS:
613                 {
614                     controlCode  = IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS;
615                     lengthNeeded = max( lengthNeeded, sizeof(SENDCMDOUTPARAMS) - 1 + READ_THRESHOLD_BUFFER_SIZE );
616                     break;
617                 }
618 
619                 case SMART_READ_LOG:
620                 {
621                     controlCode  = IOCTL_SCSI_MINIPORT_READ_SMART_LOG;
622                     lengthNeeded = max( lengthNeeded, sizeof(SENDCMDOUTPARAMS) - 1 + (SectorCount * SMART_LOG_SECTOR_SIZE) );
623                     break;
624                 }
625 
626                 case SMART_WRITE_LOG:
627                 {
628                     controlCode  = IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG;
629                     lengthNeeded = lengthNeeded - 1 + (SectorCount * SMART_LOG_SECTOR_SIZE);
630                     break;
631                 }
632 
633             }
634 
635         } else if (Command == ID_CMD) {
636 
637             controlCode  = IOCTL_SCSI_MINIPORT_IDENTIFY;
638             lengthNeeded = max( lengthNeeded, sizeof(SENDCMDOUTPARAMS) - 1 + IDENTIFY_BUFFER_SIZE );
639 
640         } else {
641 
642             NT_ASSERT(FALSE);
643         }
644 
645         NT_ASSERT(controlCode == SrbControlCode);
646         NT_ASSERT(availableBufferSize >= lengthNeeded);
647     }
648 
649 #endif
650 
651     //
652     // Build SrbControl and input to SMART command
653     //
654     SrbControl->HeaderLength = sizeof(SRB_IO_CONTROL);
655     RtlMoveMemory (SrbControl->Signature, "SCSIDISK", 8);
656     SrbControl->Timeout      = FdoExtension->TimeOutValue;
657     SrbControl->Length       = availableBufferSize;
658     SrbControl->ControlCode  = SrbControlCode;
659 
660     cmdInParameters->cBufferSize  = sizeof(SENDCMDINPARAMS);
661     cmdInParameters->bDriveNumber = diskData->ScsiAddress.TargetId;
662     cmdInParameters->irDriveRegs.bFeaturesReg     = Feature;
663     cmdInParameters->irDriveRegs.bSectorCountReg  = SectorCount;
664     cmdInParameters->irDriveRegs.bSectorNumberReg = SectorNumber;
665     cmdInParameters->irDriveRegs.bCylLowReg       = SMART_CYL_LOW;
666     cmdInParameters->irDriveRegs.bCylHighReg      = SMART_CYL_HI;
667     cmdInParameters->irDriveRegs.bCommandReg      = Command;
668 
669     //
670     // Create and send irp
671     //
672     KeInitializeEvent(&event, NotificationEvent, FALSE);
673 
674     startingOffset.QuadPart = (LONGLONG) 1;
675 
676     length = SrbControl->HeaderLength + SrbControl->Length;
677 
678     irp = IoBuildSynchronousFsdRequest(
679                 IRP_MJ_SCSI,
680                 commonExtension->LowerDeviceObject,
681                 SrbControl,
682                 length,
683                 &startingOffset,
684                 &event,
685                 &ioStatus);
686 
687     if (irp == NULL) {
688         return STATUS_INSUFFICIENT_RESOURCES;
689     }
690 
691     irpStack = IoGetNextIrpStackLocation(irp);
692 
693     //
694     // Set major and minor codes.
695     //
696 
697     irpStack->MajorFunction = IRP_MJ_SCSI;
698     irpStack->MinorFunction = 1;
699 
700     //
701     // Fill in SRB fields.
702     //
703 
704     if (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
705         irpStack->Parameters.Others.Argument1 = srbEx;
706 
707         //
708         // Set up STORAGE_REQUEST_BLOCK fields
709         //
710 
711         srbEx->Length = FIELD_OFFSET(STORAGE_REQUEST_BLOCK, Signature);
712         srbEx->Function = SRB_FUNCTION_STORAGE_REQUEST_BLOCK;
713         srbEx->Signature = SRB_SIGNATURE;
714         srbEx->Version = STORAGE_REQUEST_BLOCK_VERSION_1;
715         srbEx->SrbLength = sizeof(srbExBuffer);
716         srbEx->SrbFunction = SRB_FUNCTION_IO_CONTROL;
717         srbEx->RequestPriority = IoGetIoPriorityHint(irp);
718         srbEx->AddressOffset = sizeof(STORAGE_REQUEST_BLOCK);
719 
720         srbEx->SrbFlags = FdoExtension->SrbFlags;
721         SET_FLAG(srbEx->SrbFlags, SRB_FLAGS_DATA_IN);
722         SET_FLAG(srbEx->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
723         SET_FLAG(srbEx->SrbFlags, SRB_FLAGS_NO_KEEP_AWAKE);
724 
725         srbEx->RequestAttribute = SRB_SIMPLE_TAG_REQUEST;
726         srbEx->RequestTag = SP_UNTAGGED;
727 
728         srbEx->OriginalRequest = irp;
729 
730         //
731         // Set timeout to requested value.
732         //
733 
734         srbEx->TimeOutValue = SrbControl->Timeout;
735 
736         //
737         // Set the data buffer.
738         //
739 
740         srbEx->DataBuffer = SrbControl;
741         srbEx->DataTransferLength = length;
742 
743         //
744         // Set up address fields
745         //
746 
747         storAddrBtl8 = (PSTOR_ADDR_BTL8) ((PUCHAR)srbEx + srbEx->AddressOffset);
748         storAddrBtl8->Type = STOR_ADDRESS_TYPE_BTL8;
749         storAddrBtl8->AddressLength = STOR_ADDR_BTL8_ADDRESS_LENGTH;
750         storAddrBtl8->Path = diskData->ScsiAddress.PathId;
751         storAddrBtl8->Target = diskData->ScsiAddress.TargetId;
752         storAddrBtl8->Lun = srb.Lun = diskData->ScsiAddress.Lun;
753 
754     } else {
755         irpStack->Parameters.Others.Argument1 = &srb;
756 
757         srb.PathId = diskData->ScsiAddress.PathId;
758         srb.TargetId = diskData->ScsiAddress.TargetId;
759         srb.Lun = diskData->ScsiAddress.Lun;
760 
761         srb.Function = SRB_FUNCTION_IO_CONTROL;
762         srb.Length = sizeof(SCSI_REQUEST_BLOCK);
763 
764         srb.SrbFlags = FdoExtension->SrbFlags;
765         SET_FLAG(srb.SrbFlags, SRB_FLAGS_DATA_IN);
766         SET_FLAG(srb.SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
767         SET_FLAG(srb.SrbFlags, SRB_FLAGS_NO_KEEP_AWAKE);
768 
769         srb.QueueAction = SRB_SIMPLE_TAG_REQUEST;
770         srb.QueueTag = SP_UNTAGGED;
771 
772         srb.OriginalRequest = irp;
773 
774         //
775         // Set timeout to requested value.
776         //
777 
778         srb.TimeOutValue = SrbControl->Timeout;
779 
780         //
781         // Set the data buffer.
782         //
783 
784         srb.DataBuffer = SrbControl;
785         srb.DataTransferLength = length;
786     }
787 
788     //
789     // Flush the data buffer for output. This will insure that the data is
790     // written back to memory.  Since the data-in flag is the the port driver
791     // will flush the data again for input which will ensure the data is not
792     // in the cache.
793     //
794 
795     KeFlushIoBuffers(irp->MdlAddress, FALSE, TRUE);
796 
797     //
798     // Call port driver to handle this request.
799     //
800 
801     status = IoCallDriver(commonExtension->LowerDeviceObject, irp);
802 
803     if (status == STATUS_PENDING) {
804         KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
805         status = ioStatus.Status;
806     }
807 
808     return status;
809 }
810 
811 
812 NTSTATUS
DiskGetIdentifyInfo(PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,PBOOLEAN SupportSmart)813 DiskGetIdentifyInfo(
814     PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
815     PBOOLEAN SupportSmart
816     )
817 {
818     UCHAR outBuffer[sizeof(SRB_IO_CONTROL) + max( sizeof(SENDCMDINPARAMS), sizeof(SENDCMDOUTPARAMS) - 1 + IDENTIFY_BUFFER_SIZE )] = {0};
819     ULONG outBufferSize = sizeof(outBuffer);
820     NTSTATUS status;
821 
822     PAGED_CODE();
823 
824     status = DiskGetIdentifyData(FdoExtension,
825                                  (PSRB_IO_CONTROL)outBuffer,
826                                  &outBufferSize);
827 
828     if (NT_SUCCESS(status))
829     {
830         PUSHORT identifyData = (PUSHORT)&(outBuffer[sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDOUTPARAMS) - 1]);
831         USHORT commandSetSupported = identifyData[82];
832 
833         *SupportSmart = ((commandSetSupported != 0xffff) &&
834                          (commandSetSupported != 0) &&
835                          ((commandSetSupported & 1) == 1));
836     } else {
837         *SupportSmart = FALSE;
838     }
839 
840     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DiskGetIdentifyInfo: SMART %s supported for device %p, status %lx\n",
841                    *SupportSmart ? "is" : "is not",
842                    FdoExtension->DeviceObject,
843                    status));
844 
845     return status;
846 }
847 
848 
849 //
850 // FP Ioctl specific routines
851 //
852 
853 NTSTATUS
DiskSendFailurePredictIoctl(PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,PSTORAGE_PREDICT_FAILURE checkFailure)854 DiskSendFailurePredictIoctl(
855     PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
856     PSTORAGE_PREDICT_FAILURE checkFailure
857     )
858 {
859     KEVENT event;
860     PDEVICE_OBJECT deviceObject;
861     IO_STATUS_BLOCK ioStatus = { 0 };
862     PIRP irp;
863     NTSTATUS status;
864 
865     PAGED_CODE();
866 
867     KeInitializeEvent(&event, SynchronizationEvent, FALSE);
868 
869     deviceObject = IoGetAttachedDeviceReference(FdoExtension->DeviceObject);
870 
871     irp = IoBuildDeviceIoControlRequest(
872                     IOCTL_STORAGE_PREDICT_FAILURE,
873                     deviceObject,
874                     NULL,
875                     0,
876                     checkFailure,
877                     sizeof(STORAGE_PREDICT_FAILURE),
878                     FALSE,
879                     &event,
880                     &ioStatus);
881 
882     if (irp != NULL)
883     {
884         status = IoCallDriver(deviceObject, irp);
885         if (status == STATUS_PENDING)
886         {
887             KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
888             status = ioStatus.Status;
889         }
890 
891     } else {
892         status = STATUS_INSUFFICIENT_RESOURCES;
893     }
894 
895     ObDereferenceObject(deviceObject);
896 
897     return status;
898 }
899 
900 #if (NTDDI_VERSION >= NTDDI_WINBLUE)
901 
902 NTSTATUS
DiskGetModePage(_In_ PDEVICE_OBJECT Fdo,_In_ UCHAR PageMode,_In_ UCHAR PageControl,_In_ PMODE_PARAMETER_HEADER ModeData,_Inout_ PULONG ModeDataSize,_Out_ PVOID * PageData)903 DiskGetModePage(
904     _In_ PDEVICE_OBJECT Fdo,
905     _In_ UCHAR PageMode,
906     _In_ UCHAR PageControl,
907     _In_ PMODE_PARAMETER_HEADER ModeData,
908     _Inout_ PULONG ModeDataSize,
909     _Out_ PVOID* PageData
910     )
911 {
912     ULONG size = 0;
913     PVOID pageData = NULL;
914 
915     PAGED_CODE();
916 
917     if (ModeData == NULL ||
918         ModeDataSize == NULL ||
919         *ModeDataSize < sizeof(MODE_PARAMETER_HEADER) ||
920         PageData == NULL) {
921         return STATUS_INVALID_PARAMETER;
922     }
923 
924     RtlZeroMemory (ModeData, *ModeDataSize);
925 
926     size = ClassModeSenseEx(Fdo,
927                             (PCHAR) ModeData,
928                             *ModeDataSize,
929                             PageMode,
930                             PageControl);
931 
932     if (size < sizeof(MODE_PARAMETER_HEADER)) {
933 
934         //
935         // Retry the request in case of a check condition.
936         //
937         size = ClassModeSenseEx(Fdo,
938                             (PCHAR) ModeData,
939                             *ModeDataSize,
940                             PageMode,
941                             PageControl);
942 
943         if (size < sizeof(MODE_PARAMETER_HEADER)) {
944             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_WMI, "DiskGetModePage: Mode Sense for Page Mode %d with Page Control %d failed\n",
945                 PageMode, PageControl));
946             *ModeDataSize = 0;
947             return STATUS_IO_DEVICE_ERROR;
948         }
949     }
950 
951     //
952     // If the length is greater than length indicated by the mode data reset
953     // the data to the mode data.
954     //
955     if (size > (ULONG) (ModeData->ModeDataLength + 1)) {
956         size = ModeData->ModeDataLength + 1;
957     }
958 
959     *ModeDataSize = size;
960 
961     //
962     // Find the mode page
963     //
964     pageData = ClassFindModePage((PCHAR) ModeData,
965                                  size,
966                                  PageMode,
967                                  TRUE);
968 
969     if (pageData) {
970         *PageData = pageData;
971         return STATUS_SUCCESS;
972     } else {
973         *PageData = NULL;
974         return STATUS_NOT_SUPPORTED;
975     }
976 }
977 
978 NTSTATUS
DiskEnableInfoExceptions(_In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,_In_ BOOLEAN Enable)979 DiskEnableInfoExceptions(
980     _In_ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
981     _In_ BOOLEAN Enable
982     )
983 {
984     PDISK_DATA diskData = (PDISK_DATA)(FdoExtension->CommonExtension.DriverData);
985     NTSTATUS status = STATUS_NOT_SUPPORTED;
986     PMODE_PARAMETER_HEADER modeData;
987     PMODE_INFO_EXCEPTIONS pageData;
988     MODE_INFO_EXCEPTIONS changeablePageData;
989     ULONG modeDataSize;
990 
991     PAGED_CODE();
992 
993     modeDataSize = MODE_DATA_SIZE;
994 
995     modeData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
996                                          modeDataSize,
997                                          DISK_TAG_INFO_EXCEPTION);
998 
999     if (modeData == NULL) {
1000 
1001         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_WMI, "DiskEnableInfoExceptions: Unable to allocate mode "
1002                        "data buffer\n"));
1003         return STATUS_INSUFFICIENT_RESOURCES;
1004     }
1005 
1006     //
1007     // First see which data is actually changeable.
1008     //
1009     status = DiskGetModePage(FdoExtension->DeviceObject,
1010                              MODE_PAGE_FAULT_REPORTING,
1011                              1, // Page Control = 1 indicates we want changeable values.
1012                              modeData,
1013                              &modeDataSize,
1014                              (PVOID*)&pageData);
1015 
1016     if (!NT_SUCCESS(status) || pageData == NULL) {
1017         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "DiskEnableInfoExceptions: does NOT support SMART for device %p\n",
1018                   FdoExtension->DeviceObject));
1019         FREE_POOL(modeData);
1020         return STATUS_NOT_SUPPORTED;
1021     }
1022 
1023     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "DiskEnableInfoExceptions: DOES support SMART for device %p\n",
1024                 FdoExtension->DeviceObject));
1025 
1026     //
1027     // At the very least, the DEXCPT bit must be changeable.
1028     // If it's not, bail out now.
1029     //
1030     if (pageData->Dexcpt == 0) {
1031         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "DiskEnableInfoExceptions: does NOT support DEXCPT bit for device %p\n",
1032                   FdoExtension->DeviceObject));
1033         FREE_POOL(modeData);
1034         return STATUS_NOT_SUPPORTED;
1035     }
1036 
1037     //
1038     // Cache away which values are changeable.
1039     //
1040     RtlCopyMemory(&changeablePageData, pageData, sizeof(MODE_INFO_EXCEPTIONS));
1041 
1042     //
1043     // Now get the current values.
1044     //
1045     status = DiskGetModePage(FdoExtension->DeviceObject,
1046                              MODE_PAGE_FAULT_REPORTING,
1047                              0, // Page Control = 0 indicates we want current values.
1048                              modeData,
1049                              &modeDataSize,
1050                              (PVOID*)&pageData);
1051 
1052     if (!NT_SUCCESS(status) || pageData == NULL) {
1053         //
1054         // At this point we know the device supports this mode page so
1055         // assert if something goes wrong here.
1056         //
1057         NT_ASSERT(NT_SUCCESS(status) && pageData);
1058         FREE_POOL(modeData);
1059         return STATUS_NOT_SUPPORTED;
1060     }
1061 
1062     //
1063     // If the device is currently configured to not report any informational
1064     // exceptions and we cannot change the value of that field, there's
1065     // nothing to be done.
1066     //
1067     if (pageData->ReportMethod == 0 && changeablePageData.ReportMethod == 0) {
1068         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "DiskEnableInfoExceptions: MRIE field is 0 and is not changeable for device %p\n",
1069                   FdoExtension->DeviceObject));
1070         FREE_POOL(modeData);
1071         return STATUS_NOT_SUPPORTED;
1072     }
1073 
1074     //
1075     // If the PERF bit is changeable, set it now.
1076     //
1077     if (changeablePageData.Perf) {
1078         pageData->Perf = diskData->AllowFPPerfHit ? 0 : 1;
1079     }
1080 
1081     //
1082     // If the MRIE field is changeable, set it to 4 so that informational
1083     // exceptions get reported with the "Recovered Error" sense key.
1084     //
1085     if (changeablePageData.ReportMethod) {
1086         pageData->ReportMethod = 4;
1087     }
1088 
1089     //
1090     // Finally, set the DEXCPT bit appropriately to enable/disable
1091     // informational exception reporting and send the Mode Select.
1092     //
1093     pageData->Dexcpt = !Enable;
1094 
1095     status = ClassModeSelect(FdoExtension->DeviceObject,
1096                                 (PCHAR)modeData,
1097                                 modeDataSize,
1098                                 pageData->PSBit);
1099 
1100     //
1101     // Update the failure prediction state.  Note that for this particular
1102     // mode FailurePredictionNone is used when it's not enabled.
1103     //
1104     if (NT_SUCCESS(status)) {
1105         if (Enable) {
1106             diskData->FailurePredictionCapability = FailurePredictionSense;
1107             diskData->FailurePredictionEnabled = TRUE;
1108         } else {
1109             diskData->FailurePredictionCapability = FailurePredictionNone;
1110             diskData->FailurePredictionEnabled = FALSE;
1111         }
1112     }
1113 
1114     FREE_POOL(modeData);
1115 
1116     return status;
1117 }
1118 #endif
1119 
1120 
1121 //
1122 // FP type independent routines
1123 //
1124 
1125 NTSTATUS
DiskEnableDisableFailurePrediction(PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,BOOLEAN Enable)1126 DiskEnableDisableFailurePrediction(
1127     PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
1128     BOOLEAN Enable
1129     )
1130 /*++
1131 
1132 Routine Description:
1133 
1134     Enable or disable failure prediction at the hardware level
1135 
1136 Arguments:
1137 
1138     FdoExtension
1139 
1140     Enable
1141 
1142 Return Value:
1143 
1144     NT Status
1145 
1146 --*/
1147 {
1148     NTSTATUS status;
1149     PCOMMON_DEVICE_EXTENSION commonExtension = &(FdoExtension->CommonExtension);
1150     PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
1151 
1152     PAGED_CODE();
1153 
1154     switch(diskData->FailurePredictionCapability)
1155     {
1156         case FailurePredictionSmart:
1157         {
1158             if (Enable)
1159             {
1160                 status = DiskEnableSmart(FdoExtension);
1161             } else {
1162                 status = DiskDisableSmart(FdoExtension);
1163             }
1164 
1165             if (NT_SUCCESS(status)) {
1166                 diskData->FailurePredictionEnabled = Enable;
1167             }
1168 
1169             break;
1170         }
1171 
1172         case  FailurePredictionSense:
1173         case  FailurePredictionIoctl:
1174         {
1175             //
1176             // We assume that the drive is already setup properly for
1177             // failure prediction
1178             //
1179             status = STATUS_SUCCESS;
1180             break;
1181         }
1182 
1183         default:
1184         {
1185             status = STATUS_INVALID_DEVICE_REQUEST;
1186         }
1187     }
1188     return status;
1189 }
1190 
1191 
1192 NTSTATUS
DiskEnableDisableFailurePredictPolling(PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,BOOLEAN Enable,ULONG PollTimeInSeconds)1193 DiskEnableDisableFailurePredictPolling(
1194     PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
1195     BOOLEAN Enable,
1196     ULONG PollTimeInSeconds
1197     )
1198 /*++
1199 
1200 Routine Description:
1201 
1202     Enable or disable polling for hardware failure detection
1203 
1204 Arguments:
1205 
1206     FdoExtension
1207 
1208     Enable
1209 
1210     PollTimeInSeconds - if 0 then no change to current polling timer
1211 
1212 Return Value:
1213 
1214     NT Status
1215 
1216 --*/
1217 {
1218     NTSTATUS status;
1219     PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)FdoExtension;
1220     PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
1221 
1222     PAGED_CODE();
1223 
1224     if (Enable)
1225     {
1226         status = DiskEnableDisableFailurePrediction(FdoExtension,
1227                                            Enable);
1228     } else {
1229         status = STATUS_SUCCESS;
1230     }
1231 
1232     if (NT_SUCCESS(status))
1233     {
1234         status = ClassSetFailurePredictionPoll(FdoExtension,
1235                         Enable ? diskData->FailurePredictionCapability :
1236                                  FailurePredictionNone,
1237                                      PollTimeInSeconds);
1238 
1239         //
1240         // Even if this failed we do not want to disable FP on the
1241         // hardware. FP is only ever disabled on the hardware by
1242         // specific command of the user.
1243         //
1244     }
1245 
1246     return status;
1247 }
1248 
1249 
1250 NTSTATUS
DiskReadFailurePredictStatus(PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,PSTORAGE_FAILURE_PREDICT_STATUS DiskSmartStatus)1251 DiskReadFailurePredictStatus(
1252     PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
1253     PSTORAGE_FAILURE_PREDICT_STATUS DiskSmartStatus
1254     )
1255 /*++
1256 
1257 Routine Description:
1258 
1259     Obtains current failure prediction status
1260 
1261 Arguments:
1262 
1263     FdoExtension
1264 
1265     DiskSmartStatus
1266 
1267 Return Value:
1268 
1269     NT Status
1270 
1271 --*/
1272 {
1273     PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)FdoExtension;
1274     PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
1275     NTSTATUS status;
1276 
1277     PAGED_CODE();
1278 
1279     DiskSmartStatus->PredictFailure = FALSE;
1280 
1281     switch(diskData->FailurePredictionCapability)
1282     {
1283         case FailurePredictionSmart:
1284         {
1285             UCHAR outBuffer[sizeof(SRB_IO_CONTROL) + max( sizeof(SENDCMDINPARAMS), sizeof(SENDCMDOUTPARAMS) - 1 + sizeof(IDEREGS) )] = {0};
1286             ULONG outBufferSize = sizeof(outBuffer);
1287             PSENDCMDOUTPARAMS cmdOutParameters;
1288 
1289             status = DiskReadSmartStatus(FdoExtension,
1290                                      (PSRB_IO_CONTROL)outBuffer,
1291                                      &outBufferSize);
1292 
1293             if (NT_SUCCESS(status))
1294             {
1295                 cmdOutParameters = (PSENDCMDOUTPARAMS)(outBuffer +
1296                                                sizeof(SRB_IO_CONTROL));
1297 
1298                 DiskSmartStatus->Reason = 0; // Unknown;
1299                 DiskSmartStatus->PredictFailure = ((cmdOutParameters->bBuffer[3] == 0xf4) &&
1300                                                    (cmdOutParameters->bBuffer[4] == 0x2c));
1301             }
1302             break;
1303         }
1304 
1305         case FailurePredictionSense:
1306         {
1307             DiskSmartStatus->Reason = FdoExtension->FailureReason;
1308             DiskSmartStatus->PredictFailure = FdoExtension->FailurePredicted;
1309             status = STATUS_SUCCESS;
1310             break;
1311         }
1312 
1313         case FailurePredictionIoctl:
1314         case FailurePredictionNone:
1315         default:
1316         {
1317             status = STATUS_INVALID_DEVICE_REQUEST;
1318             break;
1319         }
1320     }
1321 
1322     return status;
1323 }
1324 
1325 
1326 NTSTATUS
DiskReadFailurePredictData(PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,PSTORAGE_FAILURE_PREDICT_DATA DiskSmartData)1327 DiskReadFailurePredictData(
1328     PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
1329     PSTORAGE_FAILURE_PREDICT_DATA DiskSmartData
1330     )
1331 /*++
1332 
1333 Routine Description:
1334 
1335     Obtains current failure prediction data. Not available for
1336     FAILURE_PREDICT_SENSE types.
1337 
1338 Arguments:
1339 
1340     FdoExtension
1341 
1342     DiskSmartData
1343 
1344 Return Value:
1345 
1346     NT Status
1347 
1348 --*/
1349 {
1350     PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)FdoExtension;
1351     PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
1352     NTSTATUS status;
1353 
1354     PAGED_CODE();
1355 
1356     switch(diskData->FailurePredictionCapability)
1357     {
1358         case FailurePredictionSmart:
1359         {
1360             PUCHAR outBuffer;
1361             ULONG outBufferSize;
1362             PSENDCMDOUTPARAMS cmdOutParameters;
1363 
1364             outBufferSize = sizeof(SRB_IO_CONTROL) + max( sizeof(SENDCMDINPARAMS), sizeof(SENDCMDOUTPARAMS) - 1 + READ_ATTRIBUTE_BUFFER_SIZE );
1365 
1366             outBuffer = ExAllocatePoolWithTag(NonPagedPoolNx,
1367                                               outBufferSize,
1368                                               DISK_TAG_SMART);
1369 
1370             if (outBuffer != NULL)
1371             {
1372                 status = DiskReadSmartData(FdoExtension,
1373                                            (PSRB_IO_CONTROL)outBuffer,
1374                                            &outBufferSize);
1375 
1376                 if (NT_SUCCESS(status))
1377                 {
1378                     cmdOutParameters = (PSENDCMDOUTPARAMS)(outBuffer +
1379                                                     sizeof(SRB_IO_CONTROL));
1380 
1381                     DiskSmartData->Length = READ_ATTRIBUTE_BUFFER_SIZE;
1382                     RtlCopyMemory(DiskSmartData->VendorSpecific,
1383                                   cmdOutParameters->bBuffer,
1384                                   min(READ_ATTRIBUTE_BUFFER_SIZE, sizeof(DiskSmartData->VendorSpecific)));
1385                 }
1386                 FREE_POOL(outBuffer);
1387             } else {
1388                 status = STATUS_INSUFFICIENT_RESOURCES;
1389             }
1390 
1391             break;
1392         }
1393 
1394         case FailurePredictionSense:
1395         {
1396             DiskSmartData->Length = sizeof(ULONG);
1397             *((PULONG)DiskSmartData->VendorSpecific) = FdoExtension->FailureReason;
1398 
1399             status = STATUS_SUCCESS;
1400             break;
1401         }
1402 
1403         case FailurePredictionIoctl:
1404         case FailurePredictionNone:
1405         default:
1406         {
1407             status = STATUS_INVALID_DEVICE_REQUEST;
1408             break;
1409         }
1410     }
1411 
1412     return status;
1413 }
1414 
1415 
1416 NTSTATUS
DiskReadFailurePredictThresholds(PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,PSTORAGE_FAILURE_PREDICT_THRESHOLDS DiskSmartThresholds)1417 DiskReadFailurePredictThresholds(
1418     PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
1419     PSTORAGE_FAILURE_PREDICT_THRESHOLDS DiskSmartThresholds
1420     )
1421 /*++
1422 
1423 Routine Description:
1424 
1425     Obtains current failure prediction thresholds. Not available for
1426     FAILURE_PREDICT_SENSE types.
1427 
1428 Arguments:
1429 
1430     FdoExtension
1431 
1432     DiskSmartData
1433 
1434 Return Value:
1435 
1436     NT Status
1437 
1438 --*/
1439 {
1440     PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)FdoExtension;
1441     PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
1442     NTSTATUS status;
1443 
1444     PAGED_CODE();
1445 
1446     switch(diskData->FailurePredictionCapability)
1447     {
1448         case FailurePredictionSmart:
1449         {
1450             PUCHAR outBuffer;
1451             PSENDCMDOUTPARAMS cmdOutParameters;
1452             ULONG outBufferSize;
1453 
1454             outBufferSize = sizeof(SRB_IO_CONTROL) + max( sizeof(SENDCMDINPARAMS), sizeof(SENDCMDOUTPARAMS) - 1 + READ_THRESHOLD_BUFFER_SIZE );
1455 
1456             outBuffer = ExAllocatePoolWithTag(NonPagedPoolNx,
1457                                               outBufferSize,
1458                                               DISK_TAG_SMART);
1459 
1460             if (outBuffer != NULL)
1461             {
1462                 status = DiskReadSmartThresholds(FdoExtension,
1463                                                 (PSRB_IO_CONTROL)outBuffer,
1464                                                 &outBufferSize);
1465 
1466                 if (NT_SUCCESS(status))
1467                 {
1468                     cmdOutParameters = (PSENDCMDOUTPARAMS)(outBuffer +
1469                                            sizeof(SRB_IO_CONTROL));
1470 
1471                     RtlCopyMemory(DiskSmartThresholds->VendorSpecific,
1472                                   cmdOutParameters->bBuffer,
1473                                   min(READ_THRESHOLD_BUFFER_SIZE, sizeof(DiskSmartThresholds->VendorSpecific)));
1474                 }
1475                 FREE_POOL(outBuffer);
1476             } else {
1477                 status = STATUS_INSUFFICIENT_RESOURCES;
1478             }
1479 
1480             break;
1481         }
1482 
1483         case FailurePredictionSense:
1484         case FailurePredictionIoctl:
1485         case FailurePredictionNone:
1486         default:
1487         {
1488             status = STATUS_INVALID_DEVICE_REQUEST;
1489             break;
1490         }
1491     }
1492 
1493     return status;
1494 }
1495 
1496 
1497 VOID
1498 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
DiskReregWorker(IN PDEVICE_OBJECT DevObject,IN PVOID Context)1499 DiskReregWorker(
1500     IN PDEVICE_OBJECT DevObject,
1501     IN PVOID Context
1502     )
1503 {
1504     PDISKREREGREQUEST reregRequest;
1505     NTSTATUS status;
1506     PDEVICE_OBJECT deviceObject;
1507     PIRP irp;
1508 
1509     PAGED_CODE();
1510     UNREFERENCED_PARAMETER(DevObject);
1511 
1512     NT_ASSERT(Context != NULL);
1513     _Analysis_assume_(Context != NULL);
1514 
1515     do
1516     {
1517         reregRequest = (PDISKREREGREQUEST)ExInterlockedPopEntryList(
1518                              &DiskReregHead,
1519                              &DiskReregSpinlock);
1520 
1521         if (reregRequest != NULL)
1522         {
1523             deviceObject = reregRequest->DeviceObject;
1524             irp = reregRequest->Irp;
1525 
1526             status = IoWMIRegistrationControl(deviceObject,
1527                                               WMIREG_ACTION_UPDATE_GUIDS);
1528 
1529             //
1530             // Release remove lock and free irp, now that we are done
1531             // processing this
1532             //
1533             ClassReleaseRemoveLock(deviceObject, irp);
1534 
1535             IoFreeMdl(irp->MdlAddress);
1536             IoFreeIrp(irp);
1537 
1538             FREE_POOL(reregRequest);
1539 
1540         } else {
1541 
1542             NT_ASSERTMSG("Disk Re-registration request list should not be empty", FALSE);
1543 
1544             status = STATUS_INTERNAL_ERROR;
1545         }
1546 
1547         if (!NT_SUCCESS(status))
1548         {
1549             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskReregWorker: Reregistration failed %x\n",
1550                         status));
1551         }
1552 
1553     } while (InterlockedDecrement(&DiskReregWorkItems));
1554 
1555     IoFreeWorkItem((PIO_WORKITEM)Context);
1556 }
1557 
1558 
1559 NTSTATUS
DiskInitializeReregistration(VOID)1560 DiskInitializeReregistration(
1561     VOID
1562     )
1563 {
1564     PAGED_CODE();
1565 
1566     //
1567     // Initialize the spinlock used to manage the
1568     // list of disks reregistering their guids
1569     //
1570     KeInitializeSpinLock(&DiskReregSpinlock);
1571 
1572     return(STATUS_SUCCESS);
1573 }
1574 
1575 
1576 NTSTATUS
DiskPostReregisterRequest(PDEVICE_OBJECT DeviceObject,PIRP Irp)1577 DiskPostReregisterRequest(
1578     PDEVICE_OBJECT DeviceObject,
1579     PIRP Irp
1580     )
1581 {
1582     PDISKREREGREQUEST reregRequest;
1583     PIO_WORKITEM workItem;
1584     NTSTATUS status;
1585 
1586     workItem = IoAllocateWorkItem(DeviceObject);
1587 
1588     if (!workItem) {
1589         return STATUS_INSUFFICIENT_RESOURCES;
1590     }
1591 
1592     reregRequest = ExAllocatePoolWithTag(NonPagedPoolNx,
1593                                          sizeof(DISKREREGREQUEST),
1594                                          DISK_TAG_SMART);
1595     if (reregRequest != NULL)
1596     {
1597         //
1598         // add the disk that needs reregistration to the stack of disks
1599         // to reregister. If the list is transitioning from empty to
1600         // non empty then also kick off the work item so that the
1601         // reregistration worker can do the reregister.
1602         //
1603         reregRequest->DeviceObject = DeviceObject;
1604         reregRequest->Irp = Irp;
1605         ExInterlockedPushEntryList(
1606                                    &DiskReregHead,
1607                                    &reregRequest->Next,
1608                                    &DiskReregSpinlock);
1609 
1610         if (InterlockedIncrement(&DiskReregWorkItems) == 1)
1611         {
1612             //
1613             // There is no worker routine running, queue this one.
1614             // When the work item runs, it will process the reregistration
1615             // list.
1616             //
1617 
1618             IoQueueWorkItem(workItem,
1619                             DiskReregWorker,
1620                             DelayedWorkQueue,
1621                             workItem);
1622         } else {
1623 
1624             //
1625             // There is a worker routine already running, so we
1626             // can free this unused work item.
1627             //
1628 
1629             IoFreeWorkItem(workItem);
1630         }
1631 
1632         status = STATUS_SUCCESS;
1633 
1634     } else {
1635 
1636         IoFreeWorkItem(workItem);
1637         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL, "DiskPostReregisterRequest: could not allocate reregRequest for %p\n",
1638                     DeviceObject));
1639         status = STATUS_INSUFFICIENT_RESOURCES;
1640     }
1641 
1642     return(status);
1643 }
1644 
1645 
1646 NTSTATUS
1647 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
DiskInfoExceptionComplete(PDEVICE_OBJECT DeviceObject,PIRP Irp,PVOID Context)1648 DiskInfoExceptionComplete(
1649     PDEVICE_OBJECT DeviceObject,
1650     PIRP Irp,
1651     PVOID Context
1652     )
1653 {
1654     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
1655     PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
1656     PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
1657     PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp);
1658     PSCSI_REQUEST_BLOCK srb = Context;
1659     NTSTATUS status;
1660     BOOLEAN retry;
1661     ULONG retryInterval;
1662     ULONG srbStatus;
1663     BOOLEAN freeLockAndIrp = TRUE;
1664     PVOID originalSenseInfoBuffer = irpStack->Parameters.Others.Argument3;
1665     PSTORAGE_REQUEST_BLOCK srbEx = NULL;
1666     PVOID dataBuffer = NULL;
1667     ULONG dataLength = 0;
1668     PVOID senseBuffer = NULL;
1669     UCHAR cdbLength8 = 0;
1670     ULONG cdbLength32 = 0;
1671     UCHAR senseBufferLength = 0;
1672 
1673     srbStatus = SRB_STATUS(srb->SrbStatus);
1674 
1675     if (srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) {
1676         srbEx = (PSTORAGE_REQUEST_BLOCK)srb;
1677         dataBuffer = srbEx->DataBuffer;
1678         dataLength = srbEx->DataTransferLength;
1679         if ((srbEx->SrbFunction == SRB_FUNCTION_EXECUTE_SCSI) &&
1680             (srbEx->NumSrbExData > 0)) {
1681             (void)GetSrbScsiData(srbEx, &cdbLength8, &cdbLength32, NULL, &senseBuffer, &senseBufferLength);
1682         }
1683     } else {
1684         dataBuffer = srb->DataBuffer;
1685         dataLength = srb->DataTransferLength;
1686         senseBuffer = srb->SenseInfoBuffer;
1687     }
1688 
1689     //
1690     // Check SRB status for success of completing request.
1691     // SRB_STATUS_DATA_OVERRUN also indicates success.
1692     //
1693     if ((srbStatus != SRB_STATUS_SUCCESS) &&
1694         (srbStatus != SRB_STATUS_DATA_OVERRUN))
1695     {
1696         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskInfoExceptionComplete: IRP %p, SRB %p\n", Irp, srb));
1697 
1698         if (TEST_FLAG(srb->SrbStatus, SRB_STATUS_QUEUE_FROZEN))
1699         {
1700             ClassReleaseQueue(DeviceObject);
1701         }
1702 
1703         retry = ClassInterpretSenseInfo(
1704                     DeviceObject,
1705                     srb,
1706                     irpStack->MajorFunction,
1707                      0,
1708                     MAXIMUM_RETRIES -
1709                         ((ULONG)(ULONG_PTR)irpStack->Parameters.Others.Argument4),
1710                     &status,
1711                     &retryInterval);
1712 
1713         //
1714         // If the status is verified required and the this request
1715         // should bypass verify required then retry the request.
1716         //
1717 
1718         if (TEST_FLAG(irpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME) &&
1719             status == STATUS_VERIFY_REQUIRED)
1720         {
1721             status = STATUS_IO_DEVICE_ERROR;
1722             retry = TRUE;
1723         }
1724 
1725         retry = retry && irpStack->Parameters.Others.Argument4;
1726 
1727         irpStack->Parameters.Others.Argument4 = (PVOID)((ULONG_PTR)irpStack->Parameters.Others.Argument4 - 1);
1728 
1729         if (retry)
1730         {
1731             //
1732             // Retry request.
1733             //
1734 
1735             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL, "DiskInfoExceptionComplete: Retry request %p\n", Irp));
1736 
1737             NT_ASSERT(dataBuffer == MmGetMdlVirtualAddress(Irp->MdlAddress));
1738 
1739             if (srb->Function == SRB_FUNCTION_STORAGE_REQUEST_BLOCK) {
1740 
1741                 //
1742                 // Reset byte count of transfer in SRB Extension.
1743                 //
1744                 srbEx->DataTransferLength = Irp->MdlAddress->ByteCount;
1745 
1746                 //
1747                 // Zero SRB statuses.
1748                 //
1749 
1750                 srbEx->SrbStatus = 0;
1751                 if ((srbEx->SrbFunction == SRB_FUNCTION_EXECUTE_SCSI) &&
1752                     (srbEx->NumSrbExData > 0)) {
1753                     SetSrbScsiData(srbEx, cdbLength8, cdbLength32, 0, senseBuffer, senseBufferLength);
1754                 }
1755 
1756                 //
1757                 // Set the no disconnect flag, disable synchronous data transfers and
1758                 // disable tagged queuing. This fixes some errors.
1759                 //
1760 
1761                 SET_FLAG(srbEx->SrbFlags, SRB_FLAGS_DISABLE_DISCONNECT);
1762                 SET_FLAG(srbEx->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
1763                 CLEAR_FLAG(srbEx->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE);
1764 
1765                 srbEx->RequestAttribute = SRB_SIMPLE_TAG_REQUEST;
1766                 srbEx->RequestTag = SP_UNTAGGED;
1767 
1768             } else {
1769 
1770                 //
1771                 // Reset byte count of transfer in SRB Extension.
1772                 //
1773                 srb->DataTransferLength = Irp->MdlAddress->ByteCount;
1774 
1775                 //
1776                 // Zero SRB statuses.
1777                 //
1778 
1779                 srb->SrbStatus = srb->ScsiStatus = 0;
1780 
1781                 //
1782                 // Set the no disconnect flag, disable synchronous data transfers and
1783                 // disable tagged queuing. This fixes some errors.
1784                 //
1785 
1786                 SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_DISCONNECT);
1787                 SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
1788                 CLEAR_FLAG(srb->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE);
1789 
1790                 srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
1791                 srb->QueueTag = SP_UNTAGGED;
1792             }
1793 
1794             //
1795             // Set up major SCSI function.
1796             //
1797 
1798             nextIrpStack->MajorFunction = IRP_MJ_SCSI;
1799 
1800             //
1801             // Save SRB address in next stack for port driver.
1802             //
1803 
1804             nextIrpStack->Parameters.Scsi.Srb = srb;
1805 
1806 
1807             IoSetCompletionRoutine(Irp,
1808                                    DiskInfoExceptionComplete,
1809                                    srb,
1810                                    TRUE, TRUE, TRUE);
1811 
1812             (VOID)IoCallDriver(commonExtension->LowerDeviceObject, Irp);
1813 
1814             return STATUS_MORE_PROCESSING_REQUIRED;
1815         }
1816 
1817     } else {
1818 
1819         //
1820         // Get the results from the mode sense
1821         //
1822         PMODE_INFO_EXCEPTIONS pageData;
1823         PMODE_PARAMETER_HEADER modeData;
1824         ULONG modeDataLength;
1825 
1826         modeData = dataBuffer;
1827         modeDataLength = dataLength;
1828 
1829         pageData = ClassFindModePage((PCHAR)modeData,
1830                                      modeDataLength,
1831                                      MODE_PAGE_FAULT_REPORTING,
1832                                      TRUE);
1833         if (pageData != NULL)
1834         {
1835             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "DiskInfoExceptionComplete: %p supports SMART\n",
1836                         DeviceObject));
1837 
1838             diskData->ScsiInfoExceptionsSupported = TRUE;
1839 
1840             //
1841             // The DEXCPT bit must be 0 and the MRIE field must be valid.
1842             //
1843             if (pageData->Dexcpt == 0 &&
1844                 pageData->ReportMethod >= 2 &&
1845                 pageData->ReportMethod <= 6)
1846             {
1847                 diskData->FailurePredictionCapability = FailurePredictionSense;
1848                 diskData->FailurePredictionEnabled = TRUE;
1849                 status = DiskPostReregisterRequest(DeviceObject, Irp);
1850 
1851                 if (NT_SUCCESS(status))
1852                 {
1853                     //
1854                     // Make sure we won't free the remove lock and the irp
1855                     // since we need to keep these until after the work
1856                     // item has completed running
1857                     //
1858                     freeLockAndIrp = FALSE;
1859                 }
1860             } else {
1861                 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_WMI, "DiskInfoExceptionComplete: %p is not enabled for SMART\n",
1862                         DeviceObject));
1863 
1864             }
1865 
1866         } else {
1867             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_WMI, "DiskInfoExceptionComplete: %p does not supports SMART\n",
1868                         DeviceObject));
1869 
1870         }
1871 
1872         //
1873         // Set status for successful request
1874         //
1875 
1876         status = STATUS_SUCCESS;
1877 
1878     } // end if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_SUCCESS)
1879 
1880     //
1881     // Free the srb
1882     //
1883     if (senseBuffer != originalSenseInfoBuffer)
1884     {
1885         //
1886         // Free the original sense info buffer in case the port driver has overwritten it
1887         //
1888         FREE_POOL(originalSenseInfoBuffer);
1889     }
1890 
1891     FREE_POOL(senseBuffer);
1892     FREE_POOL(dataBuffer);
1893     FREE_POOL(srb);
1894 
1895     if (freeLockAndIrp)
1896     {
1897         //
1898         // Set status in completing IRP.
1899         //
1900 
1901         Irp->IoStatus.Status = status;
1902 
1903         //
1904         // If pending has be returned for this irp then mark the current stack as
1905         // pending.
1906         //
1907 
1908         if (Irp->PendingReturned) {
1909             IoMarkIrpPending(Irp);
1910         }
1911 
1912         ClassReleaseRemoveLock(DeviceObject, Irp);
1913         IoFreeMdl(Irp->MdlAddress);
1914         IoFreeIrp(Irp);
1915     }
1916 
1917     return(STATUS_MORE_PROCESSING_REQUIRED);
1918 }
1919 
1920 
1921 NTSTATUS
DiskInfoExceptionCheck(PFUNCTIONAL_DEVICE_EXTENSION FdoExtension)1922 DiskInfoExceptionCheck(
1923     PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
1924     )
1925 {
1926     PUCHAR modeData;
1927     PSCSI_REQUEST_BLOCK srb;
1928     PCDB cdb;
1929     PIRP irp;
1930     PIO_STACK_LOCATION irpStack;
1931     PVOID senseInfoBuffer = NULL;
1932     UCHAR senseInfoBufferLength = 0;
1933     ULONG isRemoved;
1934     ULONG srbSize;
1935     PSTORAGE_REQUEST_BLOCK srbEx = NULL;
1936     PSTOR_ADDR_BTL8 storAddrBtl8 = NULL;
1937     PSRBEX_DATA_SCSI_CDB16 srbExDataCdb16 = NULL;
1938 
1939     modeData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
1940                                      MODE_DATA_SIZE,
1941                                      DISK_TAG_INFO_EXCEPTION);
1942     if (modeData == NULL)
1943     {
1944         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_WMI, "DiskInfoExceptionCheck: Can't allocate mode data "
1945                         "buffer\n"));
1946         return(STATUS_INSUFFICIENT_RESOURCES);
1947     }
1948 
1949     if (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
1950         srbSize = CLASS_SRBEX_SCSI_CDB16_BUFFER_SIZE;
1951     } else {
1952         srbSize = SCSI_REQUEST_BLOCK_SIZE;
1953     }
1954     srb = ExAllocatePoolWithTag(NonPagedPoolNx,
1955                                 srbSize,
1956                                 DISK_TAG_SRB);
1957     if (srb == NULL)
1958     {
1959         FREE_POOL(modeData);
1960         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_WMI, "DiskInfoExceptionCheck: Can't allocate srb "
1961                         "buffer\n"));
1962         return(STATUS_INSUFFICIENT_RESOURCES);
1963     }
1964     RtlZeroMemory(srb, srbSize);
1965 
1966     //
1967     // Sense buffer is in aligned nonpaged pool.
1968     //
1969 
1970     senseInfoBuffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
1971                                             SENSE_BUFFER_SIZE_EX,
1972                                             '7CcS');
1973 
1974     if (senseInfoBuffer == NULL)
1975     {
1976         FREE_POOL(srb);
1977         FREE_POOL(modeData);
1978         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_WMI, "DiskInfoExceptionCheck: Can't allocate request sense "
1979                         "buffer\n"));
1980         return(STATUS_INSUFFICIENT_RESOURCES);
1981     }
1982 
1983     senseInfoBufferLength = SENSE_BUFFER_SIZE_EX;
1984 
1985     //
1986     // Build device I/O control request with METHOD_NEITHER data transfer.
1987     // We'll queue a completion routine to cleanup the MDL's and such ourself.
1988     //
1989 
1990     irp = IoAllocateIrp(
1991             (CCHAR) (FdoExtension->CommonExtension.LowerDeviceObject->StackSize + 1),
1992             FALSE);
1993 
1994     if (irp == NULL)
1995     {
1996         FREE_POOL(senseInfoBuffer);
1997         FREE_POOL(srb);
1998         FREE_POOL(modeData);
1999         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_WMI, "DiskInfoExceptionCheck: Can't allocate Irp\n"));
2000         return(STATUS_INSUFFICIENT_RESOURCES);
2001     }
2002 
2003     isRemoved = ClassAcquireRemoveLock(FdoExtension->DeviceObject, irp);
2004 
2005     if (isRemoved)
2006     {
2007         ClassReleaseRemoveLock(FdoExtension->DeviceObject, irp);
2008         IoFreeIrp(irp);
2009         FREE_POOL(senseInfoBuffer);
2010         FREE_POOL(srb);
2011         FREE_POOL(modeData);
2012         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_WMI, "DiskInfoExceptionCheck: RemoveLock says isRemoved\n"));
2013         return(STATUS_DEVICE_DOES_NOT_EXIST);
2014     }
2015 
2016     //
2017     // Get next stack location.
2018     //
2019 
2020     IoSetNextIrpStackLocation(irp);
2021     irpStack = IoGetCurrentIrpStackLocation(irp);
2022     irpStack->DeviceObject = FdoExtension->DeviceObject;
2023 
2024     //
2025     // Save retry count in current Irp stack.
2026     //
2027     irpStack->Parameters.Others.Argument4 = (PVOID)MAXIMUM_RETRIES;
2028 
2029     //
2030     // Save allocated sense info buffer in case the port driver overwrites it
2031     //
2032     irpStack->Parameters.Others.Argument3 = senseInfoBuffer;
2033 
2034     irpStack = IoGetNextIrpStackLocation(irp);
2035 
2036     //
2037     // Set up SRB for execute scsi request. Save SRB address in next stack
2038     // for the port driver.
2039     //
2040 
2041     irpStack->MajorFunction = IRP_MJ_SCSI;
2042     irpStack->Parameters.Scsi.Srb = srb;
2043 
2044     IoSetCompletionRoutine(irp,
2045                            DiskInfoExceptionComplete,
2046                            srb,
2047                            TRUE,
2048                            TRUE,
2049                            TRUE);
2050 
2051     irp->MdlAddress = IoAllocateMdl( modeData,
2052                                      MODE_DATA_SIZE,
2053                                      FALSE,
2054                                      FALSE,
2055                                      irp );
2056     if (irp->MdlAddress == NULL)
2057     {
2058         ClassReleaseRemoveLock(FdoExtension->DeviceObject, irp);
2059         FREE_POOL(srb);
2060         FREE_POOL(modeData);
2061         FREE_POOL(senseInfoBuffer);
2062         IoFreeIrp( irp );
2063         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_WMI, "DiskINfoExceptionCheck: Can't allocate MDL\n"));
2064         return STATUS_INSUFFICIENT_RESOURCES;
2065     }
2066 
2067     MmBuildMdlForNonPagedPool(irp->MdlAddress);
2068 
2069     //
2070     // Build the MODE SENSE CDB.
2071     //
2072 
2073     if (FdoExtension->AdapterDescriptor->SrbType == SRB_TYPE_STORAGE_REQUEST_BLOCK) {
2074 
2075         //
2076         // Set up STORAGE_REQUEST_BLOCK fields
2077         //
2078 
2079         srbEx = (PSTORAGE_REQUEST_BLOCK)srb;
2080         srbEx->Length = FIELD_OFFSET(STORAGE_REQUEST_BLOCK, Signature);
2081         srbEx->Function = SRB_FUNCTION_STORAGE_REQUEST_BLOCK;
2082         srbEx->Signature = SRB_SIGNATURE;
2083         srbEx->Version = STORAGE_REQUEST_BLOCK_VERSION_1;
2084         srbEx->SrbLength = srbSize;
2085         srbEx->SrbFunction = SRB_FUNCTION_EXECUTE_SCSI;
2086         srbEx->RequestPriority = IoGetIoPriorityHint(irp);
2087         srbEx->AddressOffset = sizeof(STORAGE_REQUEST_BLOCK);
2088         srbEx->NumSrbExData = 1;
2089 
2090         // Set timeout value from device extension.
2091         srbEx->TimeOutValue = FdoExtension->TimeOutValue;
2092 
2093         // Set the transfer length.
2094         srbEx->DataTransferLength = MODE_DATA_SIZE;
2095         srbEx->DataBuffer = modeData;
2096 
2097         srbEx->SrbFlags = FdoExtension->SrbFlags;
2098 
2099         SET_FLAG(srbEx->SrbFlags, SRB_FLAGS_DATA_IN);
2100 
2101         //
2102         // Disable synchronous transfer for these requests.
2103         //
2104         SET_FLAG(srbEx->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
2105 
2106         //
2107         // Don't freeze the queue on an error
2108         //
2109         SET_FLAG(srbEx->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
2110 
2111         srbEx->RequestAttribute = SRB_SIMPLE_TAG_REQUEST;
2112         srbEx->RequestTag = SP_UNTAGGED;
2113 
2114         // Set up IRP Address.
2115         srbEx->OriginalRequest = irp;
2116 
2117         //
2118         // Set up address fields
2119         //
2120 
2121         storAddrBtl8 = (PSTOR_ADDR_BTL8) ((PUCHAR)srbEx + srbEx->AddressOffset);
2122         storAddrBtl8->Type = STOR_ADDRESS_TYPE_BTL8;
2123         storAddrBtl8->AddressLength = STOR_ADDR_BTL8_ADDRESS_LENGTH;
2124 
2125         //
2126         // Set up SCSI SRB extended data fields
2127         //
2128 
2129         srbEx->SrbExDataOffset[0] = sizeof(STORAGE_REQUEST_BLOCK) +
2130             sizeof(STOR_ADDR_BTL8);
2131         if ((srbEx->SrbExDataOffset[0] + sizeof(SRBEX_DATA_SCSI_CDB16)) <= srbEx->SrbLength) {
2132             srbExDataCdb16 = (PSRBEX_DATA_SCSI_CDB16)((PUCHAR)srbEx + srbEx->SrbExDataOffset[0]);
2133             srbExDataCdb16->Type = SrbExDataTypeScsiCdb16;
2134             srbExDataCdb16->Length = SRBEX_DATA_SCSI_CDB16_LENGTH;
2135             srbExDataCdb16->CdbLength = 6;
2136 
2137             // Enable auto request sense.
2138             srbExDataCdb16->SenseInfoBufferLength = senseInfoBufferLength;
2139             srbExDataCdb16->SenseInfoBuffer = senseInfoBuffer;
2140 
2141             cdb = (PCDB)srbExDataCdb16->Cdb;
2142         } else {
2143             // Should not happen
2144             NT_ASSERT(FALSE);
2145 
2146             ClassReleaseRemoveLock(FdoExtension->DeviceObject, irp);
2147             FREE_POOL(srb);
2148             FREE_POOL(modeData);
2149             FREE_POOL(senseInfoBuffer);
2150             IoFreeIrp( irp );
2151             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_WMI, "DiskINfoExceptionCheck: Insufficient extended SRB size\n"));
2152             return STATUS_INTERNAL_ERROR;
2153         }
2154 
2155     } else {
2156 
2157         //
2158         // Write length to SRB.
2159         //
2160         srb->Length = SCSI_REQUEST_BLOCK_SIZE;
2161 
2162         //
2163         // Set SCSI bus address.
2164         //
2165 
2166         srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
2167 
2168         //
2169         // Enable auto request sense.
2170         //
2171 
2172         srb->SenseInfoBufferLength = senseInfoBufferLength;
2173         srb->SenseInfoBuffer = senseInfoBuffer;
2174 
2175         //
2176         // Set timeout value from device extension.
2177         //
2178         srb->TimeOutValue = FdoExtension->TimeOutValue;
2179 
2180         //
2181         // Set the transfer length.
2182         //
2183         srb->DataTransferLength = MODE_DATA_SIZE;
2184         srb->DataBuffer = modeData;
2185 
2186         srb->SrbFlags = FdoExtension->SrbFlags;
2187 
2188         SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN);
2189 
2190         //
2191         // Disable synchronous transfer for these requests.
2192         //
2193         SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
2194 
2195         //
2196         // Don't freeze the queue on an error
2197         //
2198         SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
2199 
2200         srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
2201         srb->QueueTag = SP_UNTAGGED;
2202 
2203         //
2204         // Set up IRP Address.
2205         //
2206         srb->OriginalRequest = irp;
2207 
2208         srb->CdbLength = 6;
2209         cdb = (PCDB)srb->Cdb;
2210 
2211     }
2212 
2213     cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
2214     cdb->MODE_SENSE.PageCode = MODE_PAGE_FAULT_REPORTING;
2215     cdb->MODE_SENSE.AllocationLength = MODE_DATA_SIZE;
2216 
2217     //
2218     // Call the port driver with the request and wait for it to complete.
2219     //
2220 
2221     IoMarkIrpPending(irp);
2222     IoCallDriver(FdoExtension->CommonExtension.LowerDeviceObject,
2223                           irp);
2224 
2225     return(STATUS_PENDING);
2226 }
2227 
2228 
2229 NTSTATUS
DiskDetectFailurePrediction(PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,PFAILURE_PREDICTION_METHOD FailurePredictCapability,BOOLEAN ScsiAddressAvailable)2230 DiskDetectFailurePrediction(
2231     PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
2232     PFAILURE_PREDICTION_METHOD FailurePredictCapability,
2233     BOOLEAN ScsiAddressAvailable
2234     )
2235 /*++
2236 
2237 Routine Description:
2238 
2239     Detect if device has any failure prediction capabilities. First we
2240     check for IDE SMART capability. This is done by sending the drive an
2241     IDENTIFY command and checking if the SMART command set bit is set.
2242 
2243     Next we check if SCSI SMART (aka Information Exception Control Page,
2244     X3T10/94-190 Rev 4). This is done by querying for the Information
2245     Exception mode page.
2246 
2247     Lastly we check if the device has IOCTL failure prediction. This mechanism
2248     a filter driver implements IOCTL_STORAGE_PREDICT_FAILURE and will respond
2249     with the information in the IOCTL. We do this by sending the ioctl and
2250     if the status returned is STATUS_SUCCESS we assume that it is supported.
2251 
2252 Arguments:
2253 
2254     FdoExtension
2255 
2256     *FailurePredictCapability
2257 
2258     ScsiAddressAvailable TRUE if there is a valid SCSI_ADDRESS available
2259                          for this device, FALSE otherwise.
2260                          If FALSE we do not allow SMART IOCTLs (FailurePredictionSmart capability)
2261                          which require a valid SCSI_ADDRESS. The other capabilities
2262                          <FailurePredictionIoctl, FailurePredictionSense) do not requere
2263                          SCSI_ADDRESS so we'll still try to initialize them.
2264 
2265 Return Value:
2266 
2267     NT Status
2268 
2269 --*/
2270 {
2271     PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION)FdoExtension;
2272     PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
2273     BOOLEAN supportFP;
2274     NTSTATUS status;
2275     STORAGE_PREDICT_FAILURE checkFailure;
2276     STORAGE_FAILURE_PREDICT_STATUS diskSmartStatus;
2277 
2278     PAGED_CODE();
2279 
2280     //
2281     // Assume no failure predict mechanisms
2282     //
2283     *FailurePredictCapability = FailurePredictionNone;
2284 
2285     //
2286     // See if this is an IDE drive that supports SMART. If so enable SMART
2287     // and then ensure that it suports the SMART READ STATUS command
2288     //
2289 
2290     if (ScsiAddressAvailable)
2291     {
2292         DiskGetIdentifyInfo(FdoExtension, &supportFP);
2293 
2294         if (supportFP)
2295         {
2296             status = DiskEnableSmart(FdoExtension);
2297             if (NT_SUCCESS(status))
2298             {
2299                 *FailurePredictCapability = FailurePredictionSmart;
2300                 diskData->FailurePredictionEnabled = TRUE;
2301 
2302                 status = DiskReadFailurePredictStatus(FdoExtension,
2303                                                   &diskSmartStatus);
2304 
2305                 TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: Device %p %s IDE SMART\n",
2306                            FdoExtension->DeviceObject,
2307                            NT_SUCCESS(status) ? "does" : "does not"));
2308 
2309                 if (!NT_SUCCESS(status))
2310                 {
2311                     *FailurePredictCapability = FailurePredictionNone;
2312                     diskData->FailurePredictionEnabled = FALSE;
2313                 }
2314             }
2315             return(status);
2316         }
2317     }
2318     //
2319     // See if there is a a filter driver to intercept
2320     // IOCTL_STORAGE_PREDICT_FAILURE
2321     //
2322     status = DiskSendFailurePredictIoctl(FdoExtension,
2323                                          &checkFailure);
2324 
2325     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: Device %p %s IOCTL_STORAGE_FAILURE_PREDICT\n",
2326                        FdoExtension->DeviceObject,
2327                        NT_SUCCESS(status) ? "does" : "does not"));
2328 
2329     if (NT_SUCCESS(status))
2330     {
2331         *FailurePredictCapability = FailurePredictionIoctl;
2332         diskData->FailurePredictionEnabled = TRUE;
2333         if (checkFailure.PredictFailure)
2334         {
2335             checkFailure.PredictFailure = 512;
2336             ClassNotifyFailurePredicted(FdoExtension,
2337                                         (PUCHAR)&checkFailure,
2338                                         sizeof(checkFailure),
2339                                         (BOOLEAN)(FdoExtension->FailurePredicted == FALSE),
2340                                         0x11,
2341                                         diskData->ScsiAddress.PathId,
2342                                         diskData->ScsiAddress.TargetId,
2343                                         diskData->ScsiAddress.Lun);
2344 
2345             FdoExtension->FailurePredicted = TRUE;
2346         }
2347         return(status);
2348     }
2349 
2350     //
2351     // Finally we assume it will not be a scsi smart drive. but
2352     // we'll also send off an asynchronous mode sense so that if
2353     // it is SMART we'll reregister the device object
2354     //
2355 
2356     *FailurePredictCapability = FailurePredictionNone;
2357 
2358     DiskInfoExceptionCheck(FdoExtension);
2359 
2360     return(STATUS_SUCCESS);
2361 }
2362 
2363 
2364 NTSTATUS
2365 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
DiskWmiFunctionControl(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp,IN ULONG GuidIndex,IN CLASSENABLEDISABLEFUNCTION Function,IN BOOLEAN Enable)2366 DiskWmiFunctionControl(
2367     IN PDEVICE_OBJECT DeviceObject,
2368     IN PIRP Irp,
2369     IN ULONG GuidIndex,
2370     IN CLASSENABLEDISABLEFUNCTION Function,
2371     IN BOOLEAN Enable
2372     )
2373 /*++
2374 
2375 Routine Description:
2376 
2377     This routine is a callback into the driver to enabled or disable event
2378     generation or data block collection. A device should only expect a
2379     single enable when the first event or data consumer enables events or
2380     data collection and a single disable when the last event or data
2381     consumer disables events or data collection. Data blocks will only
2382     receive collection enable/disable if they were registered as requiring
2383     it.
2384 
2385 
2386     When NT boots, failure prediction is not automatically enabled, although
2387     it may have been persistantly enabled on a previous boot. Polling is also
2388     not automatically enabled. When the first data block that accesses SMART
2389     such as SmartStatusGuid, SmartDataGuid, SmartPerformFunction, or
2390     SmartEventGuid is accessed then SMART is automatically enabled in the
2391     hardware. Polling is enabled when SmartEventGuid is enabled and disabled
2392     when it is disabled. Hardware SMART is only disabled when the DisableSmart
2393     method is called. Polling is also disabled when this is called regardless
2394     of the status of the other guids or events.
2395 
2396 Arguments:
2397 
2398     DeviceObject is the device whose data block is being queried
2399 
2400     GuidIndex is the index into the list of guids provided when the
2401         device registered
2402 
2403     Function specifies which functionality is being enabled or disabled
2404 
2405     Enable is TRUE then the function is being enabled else disabled
2406 
2407 Return Value:
2408 
2409     status
2410 
2411 --*/
2412 {
2413     NTSTATUS status = STATUS_SUCCESS;
2414     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
2415 
2416     PAGED_CODE();
2417 
2418     if ((Function == DataBlockCollection) && Enable)
2419     {
2420         if ((GuidIndex == SmartStatusGuid) ||
2421             (GuidIndex == SmartDataGuid) ||
2422             (GuidIndex == SmartThresholdsGuid) ||
2423             (GuidIndex == SmartPerformFunction))
2424         {
2425             status = DiskEnableDisableFailurePrediction(fdoExtension,
2426                                                         TRUE);
2427             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DeviceObject %p, Irp %p Enable -> %lx\n",
2428                        DeviceObject,
2429                        Irp,
2430                        status));
2431 
2432         } else {
2433             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DeviceObject %p, Irp %p, GuidIndex %d %s for Collection\n",
2434                       DeviceObject, Irp,
2435                       GuidIndex,
2436                       Enable ? "Enabled" : "Disabled"));
2437         }
2438     } else if (Function == EventGeneration) {
2439         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DeviceObject %p, Irp %p, GuidIndex %d %s for Event Generation\n",
2440                   DeviceObject, Irp,
2441                   GuidIndex,
2442                   Enable ? "Enabled" : "Disabled"));
2443 
2444 
2445         if ((GuidIndex == SmartEventGuid) && Enable)
2446         {
2447             status = DiskEnableDisableFailurePredictPolling(fdoExtension,
2448                                                    Enable,
2449                                                    0);
2450             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DeviceObject %p, Irp %p %s -> %lx\n",
2451                        DeviceObject,
2452                        Irp,
2453                        Enable ? "DiskEnableSmartPolling" : "DiskDisableSmartPolling",
2454                        status));
2455         }
2456 
2457 #if DBG
2458     } else {
2459         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DeviceObject %p, Irp %p, GuidIndex %d %s for function %d\n",
2460                   DeviceObject, Irp,
2461                   GuidIndex,
2462                   Enable ? "Enabled" : "Disabled",
2463                   Function));
2464 #endif
2465     }
2466 
2467     status = ClassWmiCompleteRequest(DeviceObject,
2468                                      Irp,
2469                                      status,
2470                                      0,
2471                                      IO_NO_INCREMENT);
2472     return status;
2473 }
2474 
2475 
2476 
2477 NTSTATUS
2478 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
DiskFdoQueryWmiRegInfo(IN PDEVICE_OBJECT DeviceObject,OUT ULONG * RegFlags,OUT PUNICODE_STRING InstanceName)2479 DiskFdoQueryWmiRegInfo(
2480     IN PDEVICE_OBJECT DeviceObject,
2481     OUT ULONG *RegFlags,
2482     OUT PUNICODE_STRING InstanceName
2483     )
2484 /*++
2485 
2486 Routine Description:
2487 
2488     This routine is a callback into the driver to retrieve the list of
2489     guids or data blocks that the driver wants to register with WMI. This
2490     routine may not pend or block. Driver should NOT call
2491     ClassWmiCompleteRequest.
2492 
2493 Arguments:
2494 
2495     DeviceObject is the device whose data block is being queried
2496 
2497     *RegFlags returns with a set of flags that describe the guids being
2498         registered for this device. If the device wants enable and disable
2499         collection callbacks before receiving queries for the registered
2500         guids then it should return the WMIREG_FLAG_EXPENSIVE flag. Also the
2501         returned flags may specify WMIREG_FLAG_INSTANCE_PDO in which case
2502         the instance name is determined from the PDO associated with the
2503         device object. Note that the PDO must have an associated devnode. If
2504         WMIREG_FLAG_INSTANCE_PDO is not set then Name must return a unique
2505         name for the device.
2506 
2507     InstanceName returns with the instance name for the guids if
2508         WMIREG_FLAG_INSTANCE_PDO is not set in the returned *RegFlags. The
2509         caller will call ExFreePool with the buffer returned.
2510 
2511 
2512 Return Value:
2513 
2514     status
2515 
2516 --*/
2517 {
2518     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
2519     PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
2520 
2521     PAGED_CODE();
2522     UNREFERENCED_PARAMETER(InstanceName);
2523 
2524     SET_FLAG(DiskWmiFdoGuidList[SmartThresholdsGuid].Flags,  WMIREG_FLAG_REMOVE_GUID);
2525     SET_FLAG(DiskWmiFdoGuidList[ScsiInfoExceptionsGuid].Flags,  WMIREG_FLAG_REMOVE_GUID);
2526 
2527     switch (diskData->FailurePredictionCapability)
2528     {
2529         case FailurePredictionSmart:
2530         {
2531             CLEAR_FLAG(DiskWmiFdoGuidList[SmartThresholdsGuid].Flags,  WMIREG_FLAG_REMOVE_GUID);
2532             //
2533             // Fall Through
2534             //
2535         }
2536         case FailurePredictionIoctl:
2537         {
2538             CLEAR_FLAG(DiskWmiFdoGuidList[SmartStatusGuid].Flags,      WMIREG_FLAG_REMOVE_GUID);
2539             CLEAR_FLAG(DiskWmiFdoGuidList[SmartDataGuid].Flags,        WMIREG_FLAG_REMOVE_GUID);
2540             CLEAR_FLAG(DiskWmiFdoGuidList[SmartEventGuid].Flags,       WMIREG_FLAG_REMOVE_GUID);
2541             CLEAR_FLAG(DiskWmiFdoGuidList[SmartPerformFunction].Flags, WMIREG_FLAG_REMOVE_GUID);
2542 
2543             break;
2544         }
2545 
2546         case FailurePredictionSense:
2547         {
2548             CLEAR_FLAG(DiskWmiFdoGuidList[SmartStatusGuid].Flags,      WMIREG_FLAG_REMOVE_GUID);
2549             CLEAR_FLAG(DiskWmiFdoGuidList[SmartEventGuid].Flags,       WMIREG_FLAG_REMOVE_GUID);
2550             CLEAR_FLAG(DiskWmiFdoGuidList[SmartPerformFunction].Flags, WMIREG_FLAG_REMOVE_GUID);
2551             CLEAR_FLAG(DiskWmiFdoGuidList[ScsiInfoExceptionsGuid].Flags,  WMIREG_FLAG_REMOVE_GUID);
2552             SET_FLAG  (DiskWmiFdoGuidList[SmartDataGuid].Flags,        WMIREG_FLAG_REMOVE_GUID);
2553             break;
2554         }
2555 
2556 
2557         default:
2558         {
2559             SET_FLAG  (DiskWmiFdoGuidList[SmartStatusGuid].Flags,      WMIREG_FLAG_REMOVE_GUID);
2560             SET_FLAG  (DiskWmiFdoGuidList[SmartDataGuid].Flags,        WMIREG_FLAG_REMOVE_GUID);
2561             SET_FLAG  (DiskWmiFdoGuidList[SmartEventGuid].Flags,       WMIREG_FLAG_REMOVE_GUID);
2562             SET_FLAG  (DiskWmiFdoGuidList[SmartPerformFunction].Flags, WMIREG_FLAG_REMOVE_GUID);
2563             break;
2564         }
2565     }
2566 
2567     //
2568     // Use devnode for FDOs
2569     *RegFlags = WMIREG_FLAG_INSTANCE_PDO;
2570 
2571     return STATUS_SUCCESS;
2572 }
2573 
2574 
2575 NTSTATUS
2576 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
DiskFdoQueryWmiRegInfoEx(IN PDEVICE_OBJECT DeviceObject,OUT ULONG * RegFlags,OUT PUNICODE_STRING InstanceName,OUT PUNICODE_STRING MofName)2577 DiskFdoQueryWmiRegInfoEx(
2578     IN PDEVICE_OBJECT DeviceObject,
2579     OUT ULONG *RegFlags,
2580     OUT PUNICODE_STRING InstanceName,
2581     OUT PUNICODE_STRING MofName
2582     )
2583 /*++
2584 
2585 Routine Description:
2586 
2587     This routine is a callback into the driver to retrieve the list of
2588     guids or data blocks that the driver wants to register with WMI. This
2589     routine may not pend or block. Driver should NOT call
2590     ClassWmiCompleteRequest.
2591 
2592 Arguments:
2593 
2594     DeviceObject is the device whose data block is being queried
2595 
2596     *RegFlags returns with a set of flags that describe the guids being
2597         registered for this device. If the device wants enable and disable
2598         collection callbacks before receiving queries for the registered
2599         guids then it should return the WMIREG_FLAG_EXPENSIVE flag. Also the
2600         returned flags may specify WMIREG_FLAG_INSTANCE_PDO in which case
2601         the instance name is determined from the PDO associated with the
2602         device object. Note that the PDO must have an associated devnode. If
2603         WMIREG_FLAG_INSTANCE_PDO is not set then Name must return a unique
2604         name for the device.
2605 
2606     InstanceName returns with the instance name for the guids if
2607         WMIREG_FLAG_INSTANCE_PDO is not set in the returned *RegFlags. The
2608         caller will call ExFreePool with the buffer returned.
2609 
2610     MofName returns initialized with the mof resource name for the
2611         binary mof resource attached to the driver's image file. If the
2612         driver does not have a mof resource then it should leave this
2613         parameter untouched.
2614 
2615 Return Value:
2616 
2617     status
2618 
2619 --*/
2620 {
2621     NTSTATUS status;
2622 
2623     UNREFERENCED_PARAMETER(MofName);
2624 
2625     status = DiskFdoQueryWmiRegInfo(DeviceObject,
2626                                     RegFlags,
2627                                     InstanceName);
2628 
2629     //
2630     // Leave MofName alone since disk doesn't have one
2631     //
2632     return(status);
2633 }
2634 
2635 
2636 NTSTATUS
2637 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
DiskFdoQueryWmiDataBlock(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp,IN ULONG GuidIndex,IN ULONG BufferAvail,OUT PUCHAR Buffer)2638 DiskFdoQueryWmiDataBlock(
2639     IN PDEVICE_OBJECT DeviceObject,
2640     IN PIRP Irp,
2641     IN ULONG GuidIndex,
2642     IN ULONG BufferAvail,
2643     OUT PUCHAR Buffer
2644     )
2645 /*++
2646 
2647 Routine Description:
2648 
2649     This routine is a callback into the driver to query for the contents of
2650     a data block. When the driver has finished filling the data block it
2651     must call ClassWmiCompleteRequest to complete the irp. The driver can
2652     return STATUS_PENDING if the irp cannot be completed immediately.
2653 
2654 Arguments:
2655 
2656     DeviceObject is the device whose data block is being queried
2657 
2658     Irp is the Irp that makes this request
2659 
2660     GuidIndex is the index into the list of guids provided when the
2661         device registered
2662 
2663     BufferAvail on has the maximum size available to write the data
2664         block.
2665 
2666     Buffer on return is filled with the returned data block
2667 
2668 
2669 Return Value:
2670 
2671     status
2672 
2673 --*/
2674 {
2675     NTSTATUS status;
2676     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
2677     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
2678     PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
2679     ULONG sizeNeeded;
2680 
2681     PAGED_CODE();
2682 
2683     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DiskQueryWmiDataBlock, Device %p, Irp %p, GuiIndex %d\n"
2684              "      BufferAvail %lx Buffer %p\n",
2685              DeviceObject, Irp,
2686              GuidIndex, BufferAvail, Buffer));
2687 
2688     switch (GuidIndex)
2689     {
2690         case DiskGeometryGuid:
2691         {
2692             sizeNeeded = sizeof(DISK_GEOMETRY);
2693             if (BufferAvail >= sizeNeeded)
2694             {
2695                 if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA)
2696                 {
2697                     //
2698                     // Issue ReadCapacity to update device extension
2699                     // with information for current media.
2700                     status = DiskReadDriveCapacity(commonExtension->PartitionZeroExtension->DeviceObject);
2701 
2702                     //
2703                     // Note whether the drive is ready.
2704                     diskData->ReadyStatus = status;
2705 
2706                     if (!NT_SUCCESS(status))
2707                     {
2708                         break;
2709                     }
2710                 }
2711 
2712                 //
2713                 // Copy drive geometry information from device extension.
2714                 RtlMoveMemory(Buffer,
2715                               &(fdoExtension->DiskGeometry),
2716                               sizeof(DISK_GEOMETRY));
2717 
2718                 status = STATUS_SUCCESS;
2719             } else {
2720                 status = STATUS_BUFFER_TOO_SMALL;
2721             }
2722             break;
2723         }
2724 
2725         case SmartStatusGuid:
2726         {
2727             PSTORAGE_FAILURE_PREDICT_STATUS diskSmartStatus;
2728 
2729             NT_ASSERT(diskData->FailurePredictionCapability != FailurePredictionNone);
2730 
2731             sizeNeeded = sizeof(STORAGE_FAILURE_PREDICT_STATUS);
2732             if (BufferAvail >= sizeNeeded)
2733             {
2734                 STORAGE_PREDICT_FAILURE checkFailure;
2735 
2736                 diskSmartStatus = (PSTORAGE_FAILURE_PREDICT_STATUS)Buffer;
2737 
2738                 status = DiskSendFailurePredictIoctl(fdoExtension,
2739                                                      &checkFailure);
2740 
2741                 if (NT_SUCCESS(status))
2742                 {
2743                     if (diskData->FailurePredictionCapability ==
2744                                                       FailurePredictionSense)
2745                     {
2746                         diskSmartStatus->Reason =  *((PULONG)checkFailure.VendorSpecific);
2747                     } else {
2748                         diskSmartStatus->Reason =  0; // unknown
2749                     }
2750 
2751                     diskSmartStatus->PredictFailure = (checkFailure.PredictFailure != 0);
2752                 }
2753             } else {
2754                 status = STATUS_BUFFER_TOO_SMALL;
2755             }
2756             break;
2757         }
2758 
2759         case SmartDataGuid:
2760         {
2761             PSTORAGE_FAILURE_PREDICT_DATA diskSmartData;
2762 
2763             NT_ASSERT((diskData->FailurePredictionCapability ==
2764                                                   FailurePredictionSmart) ||
2765                    (diskData->FailurePredictionCapability ==
2766                                                   FailurePredictionIoctl));
2767 
2768             sizeNeeded = sizeof(STORAGE_FAILURE_PREDICT_DATA);
2769             if (BufferAvail >= sizeNeeded)
2770             {
2771                 PSTORAGE_PREDICT_FAILURE checkFailure = (PSTORAGE_PREDICT_FAILURE)Buffer;
2772 
2773                 diskSmartData = (PSTORAGE_FAILURE_PREDICT_DATA)Buffer;
2774 
2775                 status = DiskSendFailurePredictIoctl(fdoExtension,
2776                                                      checkFailure);
2777 
2778                 if (NT_SUCCESS(status))
2779                 {
2780                     diskSmartData->Length = 512;
2781                 }
2782             } else {
2783                 status = STATUS_BUFFER_TOO_SMALL;
2784             }
2785 
2786             break;
2787         }
2788 
2789         case SmartThresholdsGuid:
2790         {
2791             PSTORAGE_FAILURE_PREDICT_THRESHOLDS diskSmartThresholds;
2792 
2793             NT_ASSERT((diskData->FailurePredictionCapability ==
2794                                                   FailurePredictionSmart));
2795 
2796             sizeNeeded = sizeof(STORAGE_FAILURE_PREDICT_THRESHOLDS);
2797             if (BufferAvail >= sizeNeeded)
2798             {
2799                 diskSmartThresholds = (PSTORAGE_FAILURE_PREDICT_THRESHOLDS)Buffer;
2800                 status = DiskReadFailurePredictThresholds(fdoExtension,
2801                                                           diskSmartThresholds);
2802             } else {
2803                 status = STATUS_BUFFER_TOO_SMALL;
2804             }
2805 
2806             break;
2807         }
2808 
2809         case SmartPerformFunction:
2810         {
2811             sizeNeeded = 0;
2812             status = STATUS_SUCCESS;
2813             break;
2814         }
2815 
2816         case ScsiInfoExceptionsGuid:
2817         {
2818             PSTORAGE_SCSI_INFO_EXCEPTIONS infoExceptions;
2819             MODE_INFO_EXCEPTIONS modeInfo;
2820 
2821             NT_ASSERT((diskData->FailurePredictionCapability ==
2822                                                   FailurePredictionSense));
2823 
2824             sizeNeeded = sizeof(STORAGE_SCSI_INFO_EXCEPTIONS);
2825             if (BufferAvail >= sizeNeeded)
2826             {
2827                 infoExceptions = (PSTORAGE_SCSI_INFO_EXCEPTIONS)Buffer;
2828                 status = DiskGetInfoExceptionInformation(fdoExtension,
2829                                                          &modeInfo);
2830                 if (NT_SUCCESS(status))
2831                 {
2832                     infoExceptions->PageSavable = modeInfo.PSBit;
2833                     infoExceptions->Flags = modeInfo.Flags;
2834                     infoExceptions->MRIE = modeInfo.ReportMethod;
2835                     infoExceptions->Padding = 0;
2836                     REVERSE_BYTES(&infoExceptions->IntervalTimer,
2837                                   &modeInfo.IntervalTimer);
2838                     REVERSE_BYTES(&infoExceptions->ReportCount,
2839                                   &modeInfo.ReportCount)
2840                 }
2841             } else {
2842                 status = STATUS_BUFFER_TOO_SMALL;
2843             }
2844 
2845             break;
2846         }
2847 
2848         default:
2849         {
2850             sizeNeeded = 0;
2851             status = STATUS_WMI_GUID_NOT_FOUND;
2852         }
2853     }
2854     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DiskQueryWmiDataBlock Device %p, Irp %p returns %lx\n",
2855              DeviceObject, Irp, status));
2856 
2857     status = ClassWmiCompleteRequest(DeviceObject,
2858                                      Irp,
2859                                      status,
2860                                      sizeNeeded,
2861                                      IO_NO_INCREMENT);
2862 
2863     return status;
2864 }
2865 
2866 
2867 NTSTATUS
2868 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
DiskFdoSetWmiDataBlock(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp,IN ULONG GuidIndex,IN ULONG BufferSize,IN PUCHAR Buffer)2869 DiskFdoSetWmiDataBlock(
2870     IN PDEVICE_OBJECT DeviceObject,
2871     IN PIRP Irp,
2872     IN ULONG GuidIndex,
2873     IN ULONG BufferSize,
2874     IN PUCHAR Buffer
2875     )
2876 /*++
2877 
2878 Routine Description:
2879 
2880     This routine is a callback into the driver to query for the contents of
2881     a data block. When the driver has finished filling the data block it
2882     must call ClassWmiCompleteRequest to complete the irp. The driver can
2883     return STATUS_PENDING if the irp cannot be completed immediately.
2884 
2885 Arguments:
2886 
2887     DeviceObject is the device whose data block is being queried
2888 
2889     Irp is the Irp that makes this request
2890 
2891     GuidIndex is the index into the list of guids provided when the
2892         device registered
2893 
2894     BufferSize has the size of the data block passed
2895 
2896     Buffer has the new values for the data block
2897 
2898 
2899 Return Value:
2900 
2901     status
2902 
2903 --*/
2904 {
2905     NTSTATUS status;
2906     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
2907     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
2908     PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
2909 
2910     PAGED_CODE();
2911 
2912     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DiskSetWmiDataBlock, Device %p, Irp %p, GuiIndex %d\n"
2913              "      BufferSize %#x Buffer %p\n",
2914              DeviceObject, Irp,
2915              GuidIndex, BufferSize, Buffer));
2916 
2917     if (GuidIndex == ScsiInfoExceptionsGuid)
2918     {
2919         PSTORAGE_SCSI_INFO_EXCEPTIONS infoExceptions;
2920         MODE_INFO_EXCEPTIONS modeInfo = {0};
2921 
2922         if (BufferSize >= sizeof(STORAGE_SCSI_INFO_EXCEPTIONS))
2923         {
2924             infoExceptions = (PSTORAGE_SCSI_INFO_EXCEPTIONS)Buffer;
2925 
2926             modeInfo.PageCode = MODE_PAGE_FAULT_REPORTING;
2927             modeInfo.PageLength = sizeof(MODE_INFO_EXCEPTIONS) - 2;
2928 
2929             modeInfo.PSBit = 0;
2930             modeInfo.Flags = infoExceptions->Flags;
2931 
2932             modeInfo.ReportMethod = infoExceptions->MRIE;
2933 
2934             REVERSE_BYTES(&modeInfo.IntervalTimer[0],
2935                           &infoExceptions->IntervalTimer);
2936 
2937             REVERSE_BYTES(&modeInfo.ReportCount[0],
2938                           &infoExceptions->ReportCount);
2939 
2940             if (modeInfo.Perf == 1)
2941             {
2942                 diskData->AllowFPPerfHit = FALSE;
2943             } else {
2944                 diskData->AllowFPPerfHit = TRUE;
2945             }
2946 
2947             status = DiskSetInfoExceptionInformation(fdoExtension,
2948                                                      &modeInfo);
2949         } else {
2950             status = STATUS_INVALID_PARAMETER;
2951         }
2952 
2953     } else if (GuidIndex <= SmartThresholdsGuid)
2954     {
2955         status = STATUS_WMI_READ_ONLY;
2956     } else {
2957         status = STATUS_WMI_GUID_NOT_FOUND;
2958     }
2959 
2960     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DiskSetWmiDataBlock Device %p, Irp %p returns %lx\n",
2961              DeviceObject, Irp, status));
2962 
2963     status = ClassWmiCompleteRequest(DeviceObject,
2964                                      Irp,
2965                                      status,
2966                                      0,
2967                                      IO_NO_INCREMENT);
2968 
2969     return status;
2970 }
2971 
2972 
2973 NTSTATUS
2974 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
DiskFdoSetWmiDataItem(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp,IN ULONG GuidIndex,IN ULONG DataItemId,IN ULONG BufferSize,IN PUCHAR Buffer)2975 DiskFdoSetWmiDataItem(
2976     IN PDEVICE_OBJECT DeviceObject,
2977     IN PIRP Irp,
2978     IN ULONG GuidIndex,
2979     IN ULONG DataItemId,
2980     IN ULONG BufferSize,
2981     IN PUCHAR Buffer
2982     )
2983 /*++
2984 
2985 Routine Description:
2986 
2987     This routine is a callback into the driver to query for the contents of
2988     a data block. When the driver has finished filling the data block it
2989     must call ClassWmiCompleteRequest to complete the irp. The driver can
2990     return STATUS_PENDING if the irp cannot be completed immediately.
2991 
2992 Arguments:
2993 
2994     DeviceObject is the device whose data block is being queried
2995 
2996     Irp is the Irp that makes this request
2997 
2998     GuidIndex is the index into the list of guids provided when the
2999         device registered
3000 
3001     DataItemId has the id of the data item being set
3002 
3003     BufferSize has the size of the data item passed
3004 
3005     Buffer has the new values for the data item
3006 
3007 
3008 Return Value:
3009 
3010     status
3011 
3012 --*/
3013 {
3014     NTSTATUS status;
3015 
3016     PAGED_CODE();
3017 
3018     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DiskSetWmiDataItem, Device %p, Irp %p, GuiIndex %d, DataId %d\n"
3019              "      BufferSize %#x Buffer %p\n",
3020              DeviceObject, Irp,
3021              GuidIndex, DataItemId, BufferSize, Buffer));
3022 
3023     if (GuidIndex <= SmartThresholdsGuid)
3024     {
3025         status = STATUS_WMI_READ_ONLY;
3026     } else {
3027         status = STATUS_WMI_GUID_NOT_FOUND;
3028     }
3029 
3030     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DiskSetWmiDataItem Device %p, Irp %p returns %lx\n",
3031              DeviceObject, Irp, status));
3032 
3033     status = ClassWmiCompleteRequest(DeviceObject,
3034                                      Irp,
3035                                      status,
3036                                      0,
3037                                      IO_NO_INCREMENT);
3038 
3039     return status;
3040 }
3041 
3042 
3043 NTSTATUS
3044 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
DiskFdoExecuteWmiMethod(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp,IN ULONG GuidIndex,IN ULONG MethodId,IN ULONG InBufferSize,IN ULONG OutBufferSize,IN PUCHAR Buffer)3045 DiskFdoExecuteWmiMethod(
3046     IN PDEVICE_OBJECT DeviceObject,
3047     IN PIRP Irp,
3048     IN ULONG GuidIndex,
3049     IN ULONG MethodId,
3050     IN ULONG InBufferSize,
3051     IN ULONG OutBufferSize,
3052     IN PUCHAR Buffer
3053     )
3054 /*++
3055 
3056 Routine Description:
3057 
3058     This routine is a callback into the driver to execute a method. When the
3059     driver has finished filling the data block it must call
3060     ClassWmiCompleteRequest to complete the irp. The driver can
3061     return STATUS_PENDING if the irp cannot be completed immediately.
3062 
3063 Arguments:
3064 
3065     DeviceObject is the device whose data block is being queried
3066 
3067     Irp is the Irp that makes this request
3068 
3069     GuidIndex is the index into the list of guids provided when the
3070         device registered
3071 
3072     MethodId has the id of the method being called
3073 
3074     InBufferSize has the size of the data block passed in as the input to
3075         the method.
3076 
3077     OutBufferSize on entry has the maximum size available to write the
3078         returned data block.
3079 
3080     Buffer is filled with the returned data block
3081 
3082 
3083 Return Value:
3084 
3085     status
3086 
3087 --*/
3088 {
3089     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
3090     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
3091     PDISK_DATA diskData = (PDISK_DATA)(commonExtension->DriverData);
3092     ULONG sizeNeeded = 0;
3093     NTSTATUS status;
3094 
3095     PAGED_CODE();
3096 
3097     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DiskExecuteWmiMethod, DeviceObject %p, Irp %p, Guid Id %d, MethodId %d\n"
3098              "      InBufferSize %#x, OutBufferSize %#x, Buffer %p\n",
3099              DeviceObject, Irp,
3100              GuidIndex, MethodId, InBufferSize, OutBufferSize, Buffer));
3101 
3102     switch(GuidIndex)
3103     {
3104         case SmartPerformFunction:
3105         {
3106 
3107             NT_ASSERT((diskData->FailurePredictionCapability ==
3108                                                   FailurePredictionSmart) ||
3109                    (diskData->FailurePredictionCapability ==
3110                                                   FailurePredictionIoctl) ||
3111                    (diskData->FailurePredictionCapability ==
3112                                                   FailurePredictionSense));
3113 
3114 
3115             switch(MethodId)
3116             {
3117                 //
3118                 // void AllowPerformanceHit([in] boolean Allow)
3119                 //
3120                 case AllowDisallowPerformanceHit:
3121                 {
3122                     BOOLEAN allowPerfHit;
3123 
3124                     sizeNeeded = 0;
3125                     if (InBufferSize >= sizeof(BOOLEAN))
3126                     {
3127                         status = STATUS_SUCCESS;
3128 
3129                         allowPerfHit = *((PBOOLEAN)Buffer);
3130                         if (diskData->AllowFPPerfHit != allowPerfHit)
3131                         {
3132                             diskData->AllowFPPerfHit = allowPerfHit;
3133                             if (diskData->FailurePredictionCapability ==
3134                                 FailurePredictionSense)
3135                             {
3136                                 MODE_INFO_EXCEPTIONS modeInfo;
3137 
3138                                 status = DiskGetInfoExceptionInformation(fdoExtension,
3139                                                                          &modeInfo);
3140                                 if (NT_SUCCESS(status))
3141                                 {
3142                                     modeInfo.Perf = allowPerfHit ? 0 : 1;
3143                                     status = DiskSetInfoExceptionInformation(fdoExtension,
3144                                                                              &modeInfo);
3145                                 }
3146                             }
3147                             else
3148                             {
3149                                 status = STATUS_INVALID_DEVICE_REQUEST;
3150                             }
3151                         }
3152 
3153                         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "DiskFdoWmiExecuteMethod: AllowPerformanceHit %x for device %p --> %lx\n",
3154                                     allowPerfHit,
3155                                     fdoExtension->DeviceObject,
3156                                     status));
3157                     } else {
3158                         status = STATUS_INVALID_PARAMETER;
3159                     }
3160                     break;
3161                 }
3162 
3163                 //
3164                 // void EnableDisableHardwareFailurePrediction([in] boolean Enable)
3165                 //
3166                 case EnableDisableHardwareFailurePrediction:
3167                 {
3168                     BOOLEAN enable;
3169 
3170                     sizeNeeded = 0;
3171                     if (InBufferSize >= sizeof(BOOLEAN))
3172                     {
3173                         status = STATUS_SUCCESS;
3174                         enable = *((PBOOLEAN)Buffer);
3175                         if (!enable)
3176                         {
3177                             //
3178                             // If we are disabling we need to also disable
3179                             // polling
3180                             //
3181                             DiskEnableDisableFailurePredictPolling(
3182                                                                fdoExtension,
3183                                                                enable,
3184                                                                0);
3185                         }
3186 
3187                         status = DiskEnableDisableFailurePrediction(
3188                                                            fdoExtension,
3189                                                            enable);
3190 
3191                         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "DiskFdoWmiExecuteMethod: EnableDisableHardwareFailurePrediction: %x for device %p --> %lx\n",
3192                                     enable,
3193                                     fdoExtension->DeviceObject,
3194                                     status));
3195                     } else {
3196                         status = STATUS_INVALID_PARAMETER;
3197                     }
3198                     break;
3199                 }
3200 
3201                 //
3202                 // void EnableDisableFailurePredictionPolling(
3203                 //                               [in] uint32 Period,
3204                 //                               [in] boolean Enable)
3205                 //
3206                 case EnableDisableFailurePredictionPolling:
3207                 {
3208                     BOOLEAN enable;
3209                     ULONG period;
3210 
3211                     sizeNeeded = 0;
3212                     if (InBufferSize >= (sizeof(ULONG) + sizeof(BOOLEAN)))
3213                     {
3214                         period = *((PULONG)Buffer);
3215                         Buffer += sizeof(ULONG);
3216                         enable = *((PBOOLEAN)Buffer);
3217 
3218                            status = DiskEnableDisableFailurePredictPolling(
3219                                                                fdoExtension,
3220                                                                enable,
3221                                                                period);
3222 
3223                            TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "DiskFdoWmiExecuteMethod: EnableDisableFailurePredictionPolling: %x %x for device %p --> %lx\n",
3224                                     enable,
3225                                     period,
3226                                     fdoExtension->DeviceObject,
3227                                     status));
3228                     } else {
3229                         status = STATUS_INVALID_PARAMETER;
3230                     }
3231                     break;
3232                 }
3233 
3234                 //
3235                 // void GetFailurePredictionCapability([out] uint32 Capability)
3236                 //
3237                 case GetFailurePredictionCapability:
3238                 {
3239                     sizeNeeded = sizeof(ULONG);
3240                     if (OutBufferSize >= sizeNeeded)
3241                     {
3242                         status = STATUS_SUCCESS;
3243                         *((PFAILURE_PREDICTION_METHOD)Buffer) = diskData->FailurePredictionCapability;
3244                         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "DiskFdoWmiExecuteMethod: GetFailurePredictionCapability: %x for device %p --> %lx\n",
3245                                     *((PFAILURE_PREDICTION_METHOD)Buffer),
3246                                     fdoExtension->DeviceObject,
3247                                     status));
3248                     } else {
3249                         status = STATUS_BUFFER_TOO_SMALL;
3250                     }
3251                     break;
3252                 }
3253 
3254                 //
3255                 // void EnableOfflineDiags([out] boolean Success);
3256                 //
3257                 case EnableOfflineDiags:
3258                 {
3259                     sizeNeeded = sizeof(BOOLEAN);
3260                     if (OutBufferSize >= sizeNeeded)
3261                     {
3262                         if (diskData->FailurePredictionCapability ==
3263                                   FailurePredictionSmart)
3264                         {
3265                             //
3266                             // Initiate or resume offline diagnostics.
3267                             // This may cause a loss of performance
3268                             // to the disk, but mayincrease the amount
3269                             // of disk checking.
3270                             //
3271                             status = DiskExecuteSmartDiagnostics(fdoExtension,
3272                                                                 0);
3273 
3274                         } else {
3275                             status = STATUS_INVALID_DEVICE_REQUEST;
3276                         }
3277 
3278                         *((PBOOLEAN)Buffer) = NT_SUCCESS(status);
3279 
3280                         TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "DiskFdoWmiExecuteMethod: EnableOfflineDiags for device %p --> %lx\n",
3281                                     fdoExtension->DeviceObject,
3282                                     status));
3283                     } else {
3284                         status = STATUS_BUFFER_TOO_SMALL;
3285                     }
3286                     break;
3287                 }
3288 
3289                 //
3290                 //    void ReadLogSectors([in] uint8 LogAddress,
3291                 //        [in] uint8 SectorCount,
3292                 //        [out] uint32 Length,
3293                 //        [out, WmiSizeIs("Length")] uint8 LogSectors[]
3294                 //       );
3295                 //
3296                 case ReadLogSectors:
3297                 {
3298                     if (diskData->FailurePredictionCapability ==
3299                                   FailurePredictionSmart)
3300                     {
3301                         if (InBufferSize >= sizeof(READ_LOG_SECTORS_IN))
3302                         {
3303                             PREAD_LOG_SECTORS_IN inParams;
3304                             PREAD_LOG_SECTORS_OUT outParams;
3305                             ULONG readSize;
3306 
3307                             inParams = (PREAD_LOG_SECTORS_IN)Buffer;
3308                             readSize = inParams->SectorCount * SMART_LOG_SECTOR_SIZE;
3309                             sizeNeeded = FIELD_OFFSET(READ_LOG_SECTORS_OUT,
3310                                                   LogSectors) + readSize;
3311 
3312                             if (OutBufferSize >= sizeNeeded)
3313                             {
3314                                 outParams = (PREAD_LOG_SECTORS_OUT)Buffer;
3315                                 status = DiskReadSmartLog(fdoExtension,
3316                                                         inParams->SectorCount,
3317                                                         inParams->LogAddress,
3318                                                         outParams->LogSectors);
3319 
3320                                 if (NT_SUCCESS(status))
3321                                 {
3322                                     outParams->Length = readSize;
3323                                 } else {
3324                                     //
3325                                     // SMART command failure is
3326                                     // indicated by successful
3327                                     // execution, but no data returned
3328                                     //
3329                                     outParams->Length = 0;
3330                                     status = STATUS_SUCCESS;
3331                                 }
3332                             } else {
3333                                 status = STATUS_BUFFER_TOO_SMALL;
3334                             }
3335 
3336                         } else {
3337                             status = STATUS_INVALID_PARAMETER;
3338                         }
3339                     } else {
3340                         status = STATUS_INVALID_DEVICE_REQUEST;
3341                     }
3342                     break;
3343                 }
3344 
3345                 //    void WriteLogSectors([in] uint8 LogAddress,
3346                 //        [in] uint8 SectorCount,
3347                 //        [in] uint32 Length,
3348                 //        [in, WmiSizeIs("Length")] uint8 LogSectors[],
3349                 //        [out] boolean Success
3350                 //       );
3351                 case WriteLogSectors:
3352                 {
3353                     if (diskData->FailurePredictionCapability ==
3354                                   FailurePredictionSmart)
3355                     {
3356                         if ((LONG)InBufferSize >= FIELD_OFFSET(WRITE_LOG_SECTORS_IN,
3357                                                         LogSectors))
3358                         {
3359                             PWRITE_LOG_SECTORS_IN inParams;
3360                             PWRITE_LOG_SECTORS_OUT outParams;
3361                             ULONG writeSize;
3362 
3363                             inParams = (PWRITE_LOG_SECTORS_IN)Buffer;
3364                             writeSize = inParams->SectorCount * SMART_LOG_SECTOR_SIZE;
3365                             if (InBufferSize >= (FIELD_OFFSET(WRITE_LOG_SECTORS_IN,
3366                                                              LogSectors) +
3367                                                  writeSize))
3368                             {
3369                                 sizeNeeded = sizeof(WRITE_LOG_SECTORS_OUT);
3370 
3371                                 if (OutBufferSize >= sizeNeeded)
3372                                 {
3373                                     outParams = (PWRITE_LOG_SECTORS_OUT)Buffer;
3374                                     status = DiskWriteSmartLog(fdoExtension,
3375                                                         inParams->SectorCount,
3376                                                         inParams->LogAddress,
3377                                                         inParams->LogSectors);
3378 
3379                                     if (NT_SUCCESS(status))
3380                                     {
3381                                         outParams->Success = TRUE;
3382                                     } else {
3383                                         outParams->Success = FALSE;
3384                                         status = STATUS_SUCCESS;
3385                                     }
3386                                 } else {
3387                                     status = STATUS_BUFFER_TOO_SMALL;
3388                                 }
3389                             } else {
3390                                 status = STATUS_INVALID_PARAMETER;
3391                             }
3392                         } else {
3393                             status = STATUS_INVALID_PARAMETER;
3394                         }
3395                     } else {
3396                         status = STATUS_INVALID_DEVICE_REQUEST;
3397                     }
3398                     break;
3399                 }
3400 
3401                 //    void ExecuteSelfTest([in] uint8 Subcommand,
3402                 //         [out,
3403                 //          Values{"0", "1", "2"},
3404                 //          ValueMap{"Successful Completion",
3405                 //                   "Captive Mode Required",
3406                 //                   "Unsuccessful Completion"}
3407                 //         ]
3408                 //         uint32 ReturnCode);
3409                 case ExecuteSelfTest:
3410                 {
3411                     if (diskData->FailurePredictionCapability ==
3412                               FailurePredictionSmart)
3413                     {
3414                         if (InBufferSize >= sizeof(EXECUTE_SELF_TEST_IN))
3415                         {
3416                             sizeNeeded = sizeof(EXECUTE_SELF_TEST_OUT);
3417                             if (OutBufferSize >= sizeNeeded)
3418                             {
3419                                 PEXECUTE_SELF_TEST_IN inParam;
3420                                 PEXECUTE_SELF_TEST_OUT outParam;
3421 
3422                                 inParam = (PEXECUTE_SELF_TEST_IN)Buffer;
3423                                 outParam = (PEXECUTE_SELF_TEST_OUT)Buffer;
3424 
3425                                 if (DiskIsValidSmartSelfTest(inParam->Subcommand))
3426                                 {
3427                                    status = DiskExecuteSmartDiagnostics(fdoExtension,
3428                                                             inParam->Subcommand);
3429                                    if (NT_SUCCESS(status))
3430                                    {
3431                                        //
3432                                        // Return self test executed
3433                                        // without a problem
3434                                        //
3435                                        outParam->ReturnCode = 0;
3436                                    } else {
3437                                        //
3438                                        // Return Self test execution
3439                                        // failed status
3440                                        //
3441                                        outParam->ReturnCode = 2;
3442                                        status = STATUS_SUCCESS;
3443                                    }
3444                                 } else {
3445                                     //
3446                                     // If self test subcommand requires
3447                                     // captive mode then return that
3448                                     // status
3449                                     //
3450                                     outParam->ReturnCode = 1;
3451                                     status = STATUS_SUCCESS;
3452                                 }
3453 
3454                             } else {
3455                                 status = STATUS_BUFFER_TOO_SMALL;
3456                             }
3457 
3458                         } else {
3459                             status = STATUS_INVALID_PARAMETER;
3460                         }
3461                     } else {
3462                         status = STATUS_INVALID_DEVICE_REQUEST;
3463                     }
3464 
3465                     break;
3466                 }
3467 
3468                 default :
3469                 {
3470                     sizeNeeded = 0;
3471                     status = STATUS_WMI_ITEMID_NOT_FOUND;
3472                     break;
3473                 }
3474             }
3475 
3476             break;
3477         }
3478 
3479         case DiskGeometryGuid:
3480         case SmartStatusGuid:
3481         case SmartDataGuid:
3482         case SmartEventGuid:
3483         case SmartThresholdsGuid:
3484         case ScsiInfoExceptionsGuid:
3485         {
3486             sizeNeeded = 0;
3487             status = STATUS_INVALID_DEVICE_REQUEST;
3488             break;
3489         }
3490 
3491         default:
3492         {
3493             sizeNeeded = 0;
3494             status = STATUS_WMI_GUID_NOT_FOUND;
3495         }
3496     }
3497 
3498     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_WMI, "Disk: DiskExecuteMethod Device %p, Irp %p returns %lx\n",
3499              DeviceObject, Irp, status));
3500 
3501     status = ClassWmiCompleteRequest(DeviceObject,
3502                                      Irp,
3503                                      status,
3504                                      sizeNeeded,
3505                                      IO_NO_INCREMENT);
3506 
3507     return status;
3508 }
3509 
3510 
3511