xref: /reactos/drivers/storage/class/cdrom/scratch.c (revision 3088717b)
1 /*--
2 
3 Copyright (C) Microsoft Corporation. All rights reserved.
4 
5 Module Name:
6 
7     scratch.c
8 
9 Abstract:
10 
11     Functions for using common scratch buffer
12 
13 Environment:
14 
15     kernel mode only
16 
17 Notes:
18 
19 
20 Revision History:
21 
22 --*/
23 
24 #include "stddef.h"
25 #include "string.h"
26 
27 #include "ntddk.h"
28 #include "ntddstor.h"
29 #include "cdrom.h"
30 #include "ioctl.h"
31 #include "scratch.h"
32 #include "mmc.h"
33 
34 #ifdef DEBUG_USE_WPP
35 #include "scratch.tmh"
36 #endif
37 
38 // Forward declarations
39 EVT_WDF_REQUEST_COMPLETION_ROUTINE  ScratchBuffer_ReadWriteCompletionRoutine;
40 
41 #ifdef ALLOC_PRAGMA
42 
43 #pragma alloc_text(PAGE, ScratchBuffer_Deallocate)
44 #pragma alloc_text(PAGE, ScratchBuffer_Allocate)
45 #pragma alloc_text(PAGE, ScratchBuffer_SetupSrb)
46 #pragma alloc_text(PAGE, ScratchBuffer_ExecuteCdbEx)
47 
48 #endif
49 
_IRQL_requires_max_(APC_LEVEL)50 _IRQL_requires_max_(APC_LEVEL)
51 VOID
52 ScratchBuffer_Deallocate(
53     _Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension
54     )
55 /*++
56 
57 Routine Description:
58 
59     release all resources allocated for scratch.
60 
61 Arguments:
62 
63     DeviceExtension - device extension
64 
65 Return Value:
66 
67     none
68 
69 --*/
70 {
71     PAGED_CODE ();
72 
73     NT_ASSERT(DeviceExtension->ScratchContext.ScratchInUse == 0);
74 
75     if (DeviceExtension->ScratchContext.ScratchHistory != NULL)
76     {
77         ExFreePool(DeviceExtension->ScratchContext.ScratchHistory);
78         DeviceExtension->ScratchContext.ScratchHistory = NULL;
79     }
80     if (DeviceExtension->ScratchContext.ScratchSense != NULL)
81     {
82         ExFreePool(DeviceExtension->ScratchContext.ScratchSense);
83         DeviceExtension->ScratchContext.ScratchSense = NULL;
84     }
85     if (DeviceExtension->ScratchContext.ScratchSrb != NULL)
86     {
87         ExFreePool(DeviceExtension->ScratchContext.ScratchSrb);
88         DeviceExtension->ScratchContext.ScratchSrb = NULL;
89     }
90     if (DeviceExtension->ScratchContext.ScratchBufferSize != 0)
91     {
92         DeviceExtension->ScratchContext.ScratchBufferSize = 0;
93     }
94     if (DeviceExtension->ScratchContext.ScratchBufferMdl != NULL)
95     {
96         IoFreeMdl(DeviceExtension->ScratchContext.ScratchBufferMdl);
97         DeviceExtension->ScratchContext.ScratchBufferMdl = NULL;
98     }
99     if (DeviceExtension->ScratchContext.ScratchBuffer != NULL)
100     {
101         ExFreePool(DeviceExtension->ScratchContext.ScratchBuffer);
102         DeviceExtension->ScratchContext.ScratchBuffer = NULL;
103     }
104 
105     if (DeviceExtension->ScratchContext.PartialMdl != NULL)
106     {
107         IoFreeMdl(DeviceExtension->ScratchContext.PartialMdl);
108         DeviceExtension->ScratchContext.PartialMdl = NULL;
109     }
110 
111     if (DeviceExtension->ScratchContext.ScratchRequest != NULL)
112     {
113         PIRP irp = WdfRequestWdmGetIrp(DeviceExtension->ScratchContext.ScratchRequest);
114         if (irp->MdlAddress)
115         {
116             irp->MdlAddress = NULL;
117         }
118         WdfObjectDelete(DeviceExtension->ScratchContext.ScratchRequest);
119         DeviceExtension->ScratchContext.ScratchRequest = NULL;
120     }
121 
122     return;
123 }
124 
_IRQL_requires_max_(APC_LEVEL)125 _IRQL_requires_max_(APC_LEVEL)
126 BOOLEAN
127 ScratchBuffer_Allocate(
128     _Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension
129     )
130 /*++
131 
132 Routine Description:
133 
134     allocate resources allocated for scratch.
135 
136 Arguments:
137 
138     DeviceExtension - device extension
139 
140 Return Value:
141 
142     none
143 
144 --*/
145 {
146     NTSTATUS status = STATUS_SUCCESS;
147 
148     PAGED_CODE ();
149 
150     NT_ASSERT(DeviceExtension->ScratchContext.ScratchInUse == 0);
151 
152     // quick-exit if already allocated
153     if ((DeviceExtension->ScratchContext.ScratchBuffer     != NULL) &&
154         (DeviceExtension->ScratchContext.ScratchBufferMdl  != NULL) &&
155         (DeviceExtension->ScratchContext.ScratchBufferSize != 0)    &&
156         (DeviceExtension->ScratchContext.ScratchRequest    != NULL) &&
157         (DeviceExtension->ScratchContext.ScratchSrb        != NULL) &&
158         (DeviceExtension->ScratchContext.ScratchHistory    != NULL) &&
159         (DeviceExtension->ScratchContext.PartialMdl  != NULL)
160         )
161     {
162         return TRUE;
163     }
164 
165     // validate max transfer already determined
166     NT_ASSERT(DeviceExtension->DeviceAdditionalData.MaxPageAlignedTransferBytes != 0);
167 
168     // validate no partially-saved state
169     NT_ASSERT(DeviceExtension->ScratchContext.ScratchBuffer     == NULL);
170     NT_ASSERT(DeviceExtension->ScratchContext.ScratchBufferMdl  == NULL);
171     NT_ASSERT(DeviceExtension->ScratchContext.ScratchBufferSize == 0);
172     NT_ASSERT(DeviceExtension->ScratchContext.ScratchRequest    == NULL);
173     NT_ASSERT(DeviceExtension->ScratchContext.PartialMdl == NULL);
174 
175     // limit the scratch buffer to between 4k and 64k (so data length fits into USHORT -- req'd for many commands)
176     DeviceExtension->ScratchContext.ScratchBufferSize = min(DeviceExtension->DeviceAdditionalData.MaxPageAlignedTransferBytes, (64*1024));
177 
178     // allocate the buffer
179     if (NT_SUCCESS(status))
180     {
181         DeviceExtension->ScratchContext.ScratchBuffer = ExAllocatePoolWithTag(NonPagedPoolNx,
182                                                                               DeviceExtension->ScratchContext.ScratchBufferSize,
183                                                                               CDROM_TAG_SCRATCH);
184         if (DeviceExtension->ScratchContext.ScratchBuffer == NULL)
185         {
186             status = STATUS_INSUFFICIENT_RESOURCES;
187             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
188                         "Failed to allocate scratch buffer of %x bytes\n",
189                         DeviceExtension->ScratchContext.ScratchBufferSize
190                         ));
191         }
192         else if (BYTE_OFFSET(DeviceExtension->ScratchContext.ScratchBuffer) != 0)
193         {
194             status = STATUS_INTERNAL_ERROR;
195             TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_INIT,
196                         "Allocation of %x bytes non-paged pool was not "
197                         "allocated on page boundary?  STATUS_INTERNAL_ERROR\n",
198                         DeviceExtension->ScratchContext.ScratchBufferSize
199                         ));
200         }
201     }
202 
203     // allocate the MDL
204     if (NT_SUCCESS(status))
205     {
206         DeviceExtension->ScratchContext.ScratchBufferMdl = IoAllocateMdl(DeviceExtension->ScratchContext.ScratchBuffer,
207                                                                          DeviceExtension->ScratchContext.ScratchBufferSize,
208                                                                          FALSE, FALSE, NULL);
209         if (DeviceExtension->ScratchContext.ScratchBufferMdl == NULL)
210         {
211             status = STATUS_INSUFFICIENT_RESOURCES;
212             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
213                         "Failed to allocate MDL for %x byte buffer\n",
214                         DeviceExtension->ScratchContext.ScratchBufferSize
215                         ));
216         }
217         else
218         {
219             MmBuildMdlForNonPagedPool(DeviceExtension->ScratchContext.ScratchBufferMdl);
220         }
221     }
222 
223     // create the request
224     if (NT_SUCCESS(status))
225     {
226         WDF_OBJECT_ATTRIBUTES attributes;
227         WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes,
228                                                 CDROM_REQUEST_CONTEXT);
229 
230         status =  WdfRequestCreate(&attributes,
231                                    DeviceExtension->IoTarget,
232                                    &DeviceExtension->ScratchContext.ScratchRequest);
233 
234         if ((!NT_SUCCESS(status)) ||
235             (DeviceExtension->ScratchContext.ScratchRequest == NULL))
236         {
237             status = STATUS_INSUFFICIENT_RESOURCES;
238             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
239                         "Failed to allocate scratch MDL \n"));
240         }
241     }
242 
243     // allocate the srb
244     if (NT_SUCCESS(status))
245     {
246         DeviceExtension->ScratchContext.ScratchSrb = ExAllocatePoolWithTag(NonPagedPoolNx,
247                                                                            sizeof(SCSI_REQUEST_BLOCK),
248                                                                            CDROM_TAG_SCRATCH);
249 
250         if (DeviceExtension->ScratchContext.ScratchSrb == NULL)
251         {
252             status = STATUS_INSUFFICIENT_RESOURCES;
253             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
254                         "Failed to allocate scratch SRB\n"));
255         }
256     }
257 
258     // allocate the sense buffer
259     if (NT_SUCCESS(status))
260     {
261         DeviceExtension->ScratchContext.ScratchSense = ExAllocatePoolWithTag(NonPagedPoolNx,
262                                                                              sizeof(SENSE_DATA),
263                                                                              CDROM_TAG_SCRATCH);
264 
265         if (DeviceExtension->ScratchContext.ScratchSense == NULL)
266         {
267             status = STATUS_INSUFFICIENT_RESOURCES;
268             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
269                         "Failed to allocate scratch sense data\n"
270                         ));
271         }
272     }
273 
274     // allocate the SRB history data
275     if (NT_SUCCESS(status))
276     {
277         size_t allocationSize = sizeof(SRB_HISTORY) - sizeof(SRB_HISTORY_ITEM);
278         allocationSize += 20 * sizeof(SRB_HISTORY_ITEM);
279 
280         DeviceExtension->ScratchContext.ScratchHistory = ExAllocatePoolWithTag(NonPagedPoolNx,
281                                                                                allocationSize,
282                                                                                CDROM_TAG_SCRATCH);
283         if (DeviceExtension->ScratchContext.ScratchHistory == NULL)
284         {
285             status = STATUS_INSUFFICIENT_RESOURCES;
286             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
287                         "Failed to allocate scratch history buffer\n"
288                         ));
289         }
290         else
291         {
292             // must be initialized here...
293             RtlZeroMemory(DeviceExtension->ScratchContext.ScratchHistory, allocationSize);
294             DeviceExtension->ScratchContext.ScratchHistory->TotalHistoryCount = 20;
295         }
296     }
297 
298     // allocate the MDL
299     if (NT_SUCCESS(status))
300     {
301         ULONG transferLength = 0;
302 
303         status = RtlULongAdd(DeviceExtension->DeviceAdditionalData.MaxPageAlignedTransferBytes, PAGE_SIZE, &transferLength);
304         if (NT_SUCCESS(status))
305         {
306             DeviceExtension->ScratchContext.PartialMdlIsBuilt = FALSE;
307             DeviceExtension->ScratchContext.PartialMdl = IoAllocateMdl(NULL,
308                                                                        transferLength,
309                                                                        FALSE,
310                                                                        FALSE,
311                                                                        NULL);
312             if (DeviceExtension->ScratchContext.PartialMdl == NULL)
313             {
314                 status = STATUS_INSUFFICIENT_RESOURCES;
315                 TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
316                             "Failed to allocate MDL for %x byte buffer\n",
317                             DeviceExtension->ScratchContext.ScratchBufferSize
318                             ));
319             }
320             else
321             {
322                 NT_ASSERT(DeviceExtension->ScratchContext.PartialMdl->Size >=
323                        (CSHORT)(sizeof(MDL) + BYTES_TO_PAGES(DeviceExtension->DeviceAdditionalData.MaxPageAlignedTransferBytes) * sizeof(PFN_NUMBER)));
324             }
325         }
326         else
327         {
328             status = STATUS_INTEGER_OVERFLOW;
329         }
330     }
331 
332     // cleanup on failure
333     if (!NT_SUCCESS(status))
334     {
335         ScratchBuffer_Deallocate(DeviceExtension);
336     }
337 
338     return NT_SUCCESS(status);
339 }
340 
341 
342 VOID
ScratchBuffer_ResetItems(_Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ BOOLEAN ResetRequestHistory)343 ScratchBuffer_ResetItems(
344     _Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension,
345     _In_ BOOLEAN                    ResetRequestHistory
346     )
347 /*++
348 
349 Routine Description:
350 
351     reset scratch items for reuse.
352 
353 Arguments:
354 
355     DeviceExtension - device extension
356     ResetRequestHistory - reset history fields or not
357 
358 Return Value:
359 
360     none
361 
362 --*/
363 {
364     NTSTATUS                 status = STATUS_SUCCESS;
365     WDF_REQUEST_REUSE_PARAMS reuseParams;
366     PIRP                     irp = NULL;
367 
368     NT_ASSERT(DeviceExtension->ScratchContext.ScratchHistory    != NULL);
369     NT_ASSERT(DeviceExtension->ScratchContext.ScratchSense      != NULL);
370     NT_ASSERT(DeviceExtension->ScratchContext.ScratchSrb        != NULL);
371     NT_ASSERT(DeviceExtension->ScratchContext.ScratchRequest    != NULL);
372     NT_ASSERT(DeviceExtension->ScratchContext.ScratchBufferSize != 0);
373     NT_ASSERT(DeviceExtension->ScratchContext.ScratchBuffer     != NULL);
374     NT_ASSERT(DeviceExtension->ScratchContext.ScratchBufferMdl  != NULL);
375     NT_ASSERT(DeviceExtension->ScratchContext.ScratchInUse      != 0);
376 
377     irp = WdfRequestWdmGetIrp(DeviceExtension->ScratchContext.ScratchRequest);
378 
379     if (ResetRequestHistory)
380     {
381         PSRB_HISTORY history = DeviceExtension->ScratchContext.ScratchHistory;
382         RtlZeroMemory(history->History, sizeof(SRB_HISTORY_ITEM) * history->TotalHistoryCount);
383         history->ClassDriverUse[0] = 0;
384         history->ClassDriverUse[1] = 0;
385         history->ClassDriverUse[2] = 0;
386         history->ClassDriverUse[3] = 0;
387         history->UsedHistoryCount  = 0;
388     }
389 
390     // re-use the KMDF request object
391 
392     // deassign the MdlAddress, this is the value we assign explicitly.
393     // this is to prevent WdfRequestReuse to release the Mdl unexpectly.
394     if (irp->MdlAddress)
395     {
396         irp->MdlAddress = NULL;
397     }
398 
399     WDF_REQUEST_REUSE_PARAMS_INIT(&reuseParams, WDF_REQUEST_REUSE_NO_FLAGS, STATUS_NOT_SUPPORTED);
400     status = WdfRequestReuse(DeviceExtension->ScratchContext.ScratchRequest, &reuseParams);
401     // WDF request to format the request befor sending it
402     if (NT_SUCCESS(status))
403     {
404         // clean up completion routine.
405         WdfRequestSetCompletionRoutine(DeviceExtension->ScratchContext.ScratchRequest, NULL, NULL);
406 
407         status = WdfIoTargetFormatRequestForInternalIoctlOthers(DeviceExtension->IoTarget,
408                                                                 DeviceExtension->ScratchContext.ScratchRequest,
409                                                                 IOCTL_SCSI_EXECUTE_IN,
410                                                                 NULL, NULL,
411                                                                 NULL, NULL,
412                                                                 NULL, NULL);
413         if (!NT_SUCCESS(status))
414         {
415             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
416                        "ScratchBuffer_ResetItems: WdfIoTargetFormatRequestForInternalIoctlOthers failed, %!STATUS!\n",
417                        status));
418         }
419     }
420 
421     RtlZeroMemory(DeviceExtension->ScratchContext.ScratchSense, sizeof(SENSE_DATA));
422     RtlZeroMemory(DeviceExtension->ScratchContext.ScratchSrb, sizeof(SCSI_REQUEST_BLOCK));
423 
424     return;
425 }
426 
427 
428 NTSTATUS
ScratchBuffer_PerformNextReadWrite(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ BOOLEAN FirstTry)429 ScratchBuffer_PerformNextReadWrite(
430     _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
431     _In_ BOOLEAN                  FirstTry
432     )
433 /*++
434 
435 Routine Description:
436 
437     This function asynchronously sends the next read/write SRB down the stack.
438 
439 Arguments:
440 
441     DeviceExtension - Device extension
442 
443 Return Value:
444 
445     none
446 
447 --*/
448 {
449     PCDROM_SCRATCH_READ_WRITE_CONTEXT   readWriteContext = &DeviceExtension->ScratchContext.ScratchReadWriteContext;
450     PCDROM_REQUEST_CONTEXT              requestContext = RequestGetContext(DeviceExtension->ScratchContext.ScratchRequest);
451     WDFREQUEST                          originalRequest = requestContext->OriginalRequest;
452     NTSTATUS                            status = STATUS_SUCCESS;
453 
454     ULONG       transferSize;
455     BOOLEAN     usePartialMdl;
456 
457     transferSize = min((readWriteContext->EntireXferLen - readWriteContext->TransferedBytes), readWriteContext->MaxLength);
458 
459     if (FirstTry)
460     {
461         DeviceExtension->ScratchContext.NumRetries = 0;
462     }
463 
464     ScratchBuffer_ResetItems(DeviceExtension, FALSE);
465 
466     usePartialMdl = (readWriteContext->PacketsCount > 1 || readWriteContext->TransferedBytes > 0);
467 
468     ScratchBuffer_SetupReadWriteSrb(DeviceExtension,
469                                     originalRequest,
470                                     readWriteContext->StartingOffset,
471                                     transferSize,
472                                     readWriteContext->DataBuffer,
473                                     readWriteContext->IsRead,
474                                     usePartialMdl
475                                     );
476 
477     WdfRequestSetCompletionRoutine(DeviceExtension->ScratchContext.ScratchRequest,
478             ScratchBuffer_ReadWriteCompletionRoutine, DeviceExtension);
479 
480     status = ScratchBuffer_SendSrb(DeviceExtension, FALSE, (FirstTry ? &readWriteContext->SrbHistoryItem : NULL));
481 
482     return status;
483 }
484 
485 
486 VOID
487 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ScratchBuffer_ReadWriteTimerRoutine(struct _KDPC * Dpc,PVOID DeferredContext,PVOID SystemArgument1,PVOID SystemArgument2)488 ScratchBuffer_ReadWriteTimerRoutine(
489     struct _KDPC *Dpc,
490     PVOID         DeferredContext,
491     PVOID         SystemArgument1,
492     PVOID         SystemArgument2
493     )
494 /*++
495 
496 Routine Description:
497 
498     Timer routine for retrying read and write requests.
499 
500 Arguments:
501 
502     Timer - WDF timer
503 
504 Return Value:
505 
506     none
507 
508 --*/
509 {
510     PCDROM_DEVICE_EXTENSION             deviceExtension = NULL;
511     PCDROM_SCRATCH_READ_WRITE_CONTEXT   readWriteContext = NULL;
512     WDFREQUEST                          originalRequest = NULL;
513     PCDROM_REQUEST_CONTEXT              requestContext = NULL;
514     NTSTATUS                            status = STATUS_SUCCESS;
515     KIRQL                               oldIrql;
516 
517     UNREFERENCED_PARAMETER(Dpc);
518     UNREFERENCED_PARAMETER(SystemArgument1);
519     UNREFERENCED_PARAMETER(SystemArgument2);
520 
521     if (DeferredContext == NULL)
522     {
523         // This is impossible, but definition of KDEFERRED_ROUTINE allows optional argument,
524         // and thus OACR will complain.
525 
526         return;
527     }
528 
529     originalRequest = (WDFREQUEST) DeferredContext;
530     requestContext = RequestGetContext(originalRequest);
531 
532     KeAcquireSpinLock(&requestContext->ReadWriteCancelSpinLock, &oldIrql);
533 
534     if (!requestContext->ReadWriteIsCompleted)
535     {
536         // As the first step, unregister the cancellation routine
537         status = WdfRequestUnmarkCancelable(originalRequest);
538     }
539     else
540     {
541         status = STATUS_CANCELLED;
542     }
543 
544     KeReleaseSpinLock(&requestContext->ReadWriteCancelSpinLock, oldIrql);
545 
546     if (status != STATUS_CANCELLED)
547     {
548         deviceExtension = requestContext->DeviceExtension;
549         readWriteContext = &deviceExtension->ScratchContext.ScratchReadWriteContext;
550 
551         // We use timer only for retries, that's why the second parameter is always FALSE
552         status = ScratchBuffer_PerformNextReadWrite(deviceExtension, FALSE);
553 
554         if (!NT_SUCCESS(status))
555         {
556             ScratchBuffer_EndUse(deviceExtension);
557             RequestCompletion(deviceExtension, originalRequest, status, readWriteContext->TransferedBytes);
558         }
559     }
560 
561     //
562     // Drop the extra reference
563     //
564     WdfObjectDereference(originalRequest);
565 }
566 
567 
568 EVT_WDF_REQUEST_CANCEL  ScratchBuffer_ReadWriteEvtRequestCancel;
569 
570 VOID
571 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ScratchBuffer_ReadWriteEvtRequestCancel(_In_ WDFREQUEST Request)572 ScratchBuffer_ReadWriteEvtRequestCancel(
573     _In_ WDFREQUEST  Request
574     )
575 /*++
576 
577 Routine Description:
578 
579     Cancels a request waiting for the read/write timer to expire. This function does not
580     support cancellation of requests that have already been sent down.
581 
582 Arguments:
583 
584     Request - WDF request
585 
586 Return Value:
587 
588     none
589 
590 --*/
591 {
592     PCDROM_REQUEST_CONTEXT             requestContext = RequestGetContext(Request);
593     PCDROM_DEVICE_EXTENSION            deviceExtension = requestContext->DeviceExtension;
594     PCDROM_SCRATCH_READ_WRITE_CONTEXT  readWriteContext = &deviceExtension->ScratchContext.ScratchReadWriteContext;
595     KIRQL                              oldIrql;
596 
597     KeAcquireSpinLock(&requestContext->ReadWriteCancelSpinLock, &oldIrql);
598 
599     if (KeCancelTimer(&requestContext->ReadWriteTimer))
600     {
601        //
602        // Timer is canceled, we own the request.  Drop the reference we took before
603        // queueing the timer.
604        //
605        WdfObjectDereference(Request);
606     }
607     else
608     {
609         //
610         // Timer will run and drop the reference but it won't complete the request
611         // because we set IsCompleted to TRUE
612         //
613     }
614 
615     requestContext->ReadWriteIsCompleted = TRUE;
616 
617     KeReleaseSpinLock(&requestContext->ReadWriteCancelSpinLock, oldIrql);
618 
619     ScratchBuffer_EndUse(deviceExtension);
620 
621     // If WdfTimerStop returned TRUE, it means this request was scheduled for a retry
622     // and the retry has not happened yet. We just need to cancel it and release the scratch buffer.
623     RequestCompletion(deviceExtension, Request, STATUS_CANCELLED, readWriteContext->TransferedBytes);
624 }
625 
626 VOID
627 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
ScratchBuffer_ReadWriteCompletionRoutine(_In_ WDFREQUEST Request,_In_ WDFIOTARGET Target,_In_ PWDF_REQUEST_COMPLETION_PARAMS Params,_In_ WDFCONTEXT Context)628 ScratchBuffer_ReadWriteCompletionRoutine(
629     _In_ WDFREQUEST  Request,
630     _In_ WDFIOTARGET  Target,
631     _In_ PWDF_REQUEST_COMPLETION_PARAMS  Params,
632     _In_ WDFCONTEXT  Context
633     )
634 /*++
635 
636 Routine Description:
637 
638     Read/write request completion routine.
639 
640 Arguments:
641     Request - WDF request
642     Target - The IO target the request was completed by.
643     Params - the request completion parameters
644     Context - context
645 
646 Return Value:
647 
648     none
649 
650 --*/
651 {
652     PCDROM_DEVICE_EXTENSION             deviceExtension = (PCDROM_DEVICE_EXTENSION) Context;
653     PCDROM_SCRATCH_READ_WRITE_CONTEXT   readWriteContext = &deviceExtension->ScratchContext.ScratchReadWriteContext;
654     NTSTATUS                            status = STATUS_SUCCESS;
655     PCDROM_REQUEST_CONTEXT              requestContext = RequestGetContext(deviceExtension->ScratchContext.ScratchRequest);
656     WDFREQUEST                          originalRequest = requestContext->OriginalRequest;
657 
658     if (!NT_SUCCESS(WdfRequestGetStatus(Request)))
659     {
660         TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
661                    "WdfRequestSend: %lx\n",
662                     WdfRequestGetStatus(Request)
663                     ));
664     }
665 
666     UNREFERENCED_PARAMETER(Params);
667     UNREFERENCED_PARAMETER(Target);
668 
669     // We are not calling ScratchBuffer_BeginUse / ScratchBuffer_EndUse in this function, because we already own
670     // the scratch buffer if this function is being called.
671 
672     if ((deviceExtension->ScratchContext.ScratchSrb->SrbStatus == SRB_STATUS_ABORTED) &&
673         (deviceExtension->ScratchContext.ScratchSrb->InternalStatus == STATUS_CANCELLED))
674     {
675         // The request has been cancelled, just need to complete it
676     }
677     else if (SRB_STATUS(deviceExtension->ScratchContext.ScratchSrb->SrbStatus) != SRB_STATUS_SUCCESS)
678     {
679         // The SCSI command that we sent down has failed, retry it if necessary
680         BOOLEAN shouldRetry = TRUE;
681         LONGLONG retryIn100nsUnits = 0;
682 
683         shouldRetry = RequestSenseInfoInterpretForScratchBuffer(deviceExtension,
684                                                                 deviceExtension->ScratchContext.NumRetries,
685                                                                 &status,
686                                                                 &retryIn100nsUnits);
687 
688         if (shouldRetry)
689         {
690             deviceExtension->ScratchContext.NumRetries++;
691 
692             if (retryIn100nsUnits == 0)
693             {
694                 // We take a shortcut here by calling ScratchBuffer_PerformNextReadWrite directly:
695                 // this helps to avoid unnecessary context switch.
696                 status = ScratchBuffer_PerformNextReadWrite(deviceExtension, FALSE);
697 
698                 if (NT_SUCCESS(status))
699                 {
700                     // We're not done with the request yet, no need to complete it now
701                     return;
702                 }
703             }
704             else
705             {
706                 PCDROM_REQUEST_CONTEXT originalRequestContext = RequestGetContext(originalRequest);
707                 KIRQL                  oldIrql;
708 
709                 //
710                 // Initialize the spin lock and timer local to the original request.
711                 //
712                 if (!originalRequestContext->ReadWriteRetryInitialized)
713                 {
714                     KeInitializeSpinLock(&originalRequestContext->ReadWriteCancelSpinLock);
715                     KeInitializeTimer(&originalRequestContext->ReadWriteTimer);
716                     KeInitializeDpc(&originalRequestContext->ReadWriteDpc, ScratchBuffer_ReadWriteTimerRoutine, originalRequest);
717                     originalRequestContext->ReadWriteRetryInitialized = TRUE;
718                 }
719 
720                 KeAcquireSpinLock(&requestContext->ReadWriteCancelSpinLock, &oldIrql);
721 
722                 status = WdfRequestMarkCancelableEx(originalRequest, ScratchBuffer_ReadWriteEvtRequestCancel);
723 
724                 if (status == STATUS_CANCELLED)
725                 {
726                     requestContext->ReadWriteIsCompleted = TRUE;
727 
728                     KeReleaseSpinLock(&requestContext->ReadWriteCancelSpinLock, oldIrql);
729                 }
730                 else
731                 {
732                     LARGE_INTEGER t;
733 
734                     t.QuadPart = -retryIn100nsUnits;
735 
736                     WdfObjectReference(originalRequest);
737 
738                     // Use negative time to indicate that we want a relative delay
739                     KeSetTimer(&originalRequestContext->ReadWriteTimer,
740                                t,
741                                &originalRequestContext->ReadWriteDpc
742                                );
743 
744                     KeReleaseSpinLock(&requestContext->ReadWriteCancelSpinLock, oldIrql);
745 
746                     return;
747                 }
748             }
749         }
750     }
751     else
752     {
753         // The SCSI command has succeeded
754         readWriteContext->DataBuffer += deviceExtension->ScratchContext.ScratchSrb->DataTransferLength;
755         readWriteContext->StartingOffset.QuadPart += deviceExtension->ScratchContext.ScratchSrb->DataTransferLength;
756         readWriteContext->TransferedBytes += deviceExtension->ScratchContext.ScratchSrb->DataTransferLength;
757         readWriteContext->PacketsCount--;
758 
759         // Update the SRB history item
760         if (readWriteContext->SrbHistoryItem)
761         {
762             ULONG senseSize;
763 
764             // Query the tick count and store in the history
765             KeQueryTickCount(&readWriteContext->SrbHistoryItem->TickCountCompleted);
766 
767             // Copy the SRB Status...
768             readWriteContext->SrbHistoryItem->SrbStatus = deviceExtension->ScratchContext.ScratchSrb->SrbStatus;
769 
770             // Determine the amount of valid sense data
771             if (deviceExtension->ScratchContext.ScratchSrb->SenseInfoBufferLength >=
772                     RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength))
773             {
774                 PSENSE_DATA sense = (PSENSE_DATA)deviceExtension->ScratchContext.ScratchSrb->SenseInfoBuffer;
775                 senseSize = RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength) +
776                             sense->AdditionalSenseLength;
777                 senseSize = min(senseSize, sizeof(SENSE_DATA));
778             }
779             else
780             {
781                 senseSize = deviceExtension->ScratchContext.ScratchSrb->SenseInfoBufferLength;
782             }
783 
784             // Normalize the sense data copy in the history
785             RtlZeroMemory(&(readWriteContext->SrbHistoryItem->NormalizedSenseData), sizeof(SENSE_DATA));
786             RtlCopyMemory(&(readWriteContext->SrbHistoryItem->NormalizedSenseData),
787                     deviceExtension->ScratchContext.ScratchSrb->SenseInfoBuffer, senseSize);
788         }
789 
790         // Check whether we need to send more SCSI commands to complete the request
791         if (readWriteContext->PacketsCount > 0)
792         {
793             status = ScratchBuffer_PerformNextReadWrite(deviceExtension, TRUE);
794 
795             if (NT_SUCCESS(status))
796             {
797                 // We're not done with the request yet, no need to complete it now
798                 return;
799             }
800         }
801     }
802 
803     ScratchBuffer_EndUse(deviceExtension);
804 
805 
806     RequestCompletion(deviceExtension, originalRequest, status, readWriteContext->TransferedBytes);
807 }
808 
_IRQL_requires_max_(APC_LEVEL)809 _IRQL_requires_max_(APC_LEVEL)
810 VOID
811 ScratchBuffer_SetupSrb(
812     _Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension,
813     _In_opt_ WDFREQUEST             OriginalRequest,
814     _In_ ULONG                      MaximumTransferLength,
815     _In_ BOOLEAN                    GetDataFromDevice
816     )
817 /*++
818 
819 Routine Description:
820 
821     setup scratch SRB for sending out.
822 
823 Arguments:
824 
825     DeviceExtension - device extension
826     OriginalRequest - original request delivered by WDF
827     MaximumTransferLength - transfer length
828     GetDataFromDevice - TRUE (get data from device); FALSE (send data to device)
829 
830 Return Value:
831 
832     none
833 
834 --*/
835 {
836     WDFREQUEST              request = DeviceExtension->ScratchContext.ScratchRequest;
837     PIRP                    irp = WdfRequestWdmGetIrp(request);
838     PSCSI_REQUEST_BLOCK     srb = DeviceExtension->ScratchContext.ScratchSrb;
839     PIO_STACK_LOCATION      irpStack = NULL;
840     PCDROM_REQUEST_CONTEXT  requestContext = RequestGetContext(request);
841 
842     PAGED_CODE ();
843 
844     requestContext->OriginalRequest = OriginalRequest;
845 
846     // set to use the full scratch buffer via the scratch SRB
847     irpStack = IoGetNextIrpStackLocation(irp);
848     irpStack->MajorFunction = IRP_MJ_SCSI;
849     if (MaximumTransferLength == 0)
850     {
851         irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_NONE;
852     }
853     else if (GetDataFromDevice)
854     {
855         irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN;
856     }
857     else
858     {
859         irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_OUT;
860     }
861     irpStack->Parameters.Scsi.Srb = srb;
862 
863     if (MaximumTransferLength > 0)
864     {
865         // the Irp must show the MDL's address for the transfer
866         irp->MdlAddress = DeviceExtension->ScratchContext.ScratchBufferMdl;
867 
868         srb->DataBuffer = DeviceExtension->ScratchContext.ScratchBuffer;
869     }
870 
871     // prepare the SRB with default values
872     srb->Length = SCSI_REQUEST_BLOCK_SIZE;
873     srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
874     srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
875     srb->SrbStatus = 0;
876     srb->ScsiStatus = 0;
877     srb->NextSrb = NULL;
878     srb->OriginalRequest = irp;
879     srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
880     srb->SenseInfoBuffer = DeviceExtension->ScratchContext.ScratchSense;
881 
882     srb->CdbLength = 16; // to cause failures if not set correctly -- CD devices limited to 12 bytes for now...
883 
884     srb->DataTransferLength = min(DeviceExtension->ScratchContext.ScratchBufferSize, MaximumTransferLength);
885     srb->TimeOutValue = DeviceExtension->TimeOutValue;
886     srb->SrbFlags = DeviceExtension->SrbFlags;
887     SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
888     SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
889 
890     if (MaximumTransferLength == 0)
891     {
892         SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_DATA_TRANSFER);
893     }
894     else if (GetDataFromDevice)
895     {
896         SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN);
897     }
898     else
899     {
900         SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_OUT);
901     }
902 }
903 
904 
905 NTSTATUS
906 ScratchBuffer_SendSrb(
907     _Inout_     PCDROM_DEVICE_EXTENSION DeviceExtension,
908     _In_        BOOLEAN                 SynchronousSrb,
909     _When_(SynchronousSrb, _Pre_null_)
910     _When_(!SynchronousSrb, _In_opt_)
911                 PSRB_HISTORY_ITEM       *SrbHistoryItem
912     )
913 /*++
914 
915 Routine Description:
916 
917     Send the command from the scratch SRB to lower driver and retry if necessary.
918 
919 Arguments:
920 
921     DeviceExtension - device extension
922     SynchronousSrb - indicates whether the SRB needs to be sent synchronously or nor
923     SrbHistoryItem - storage for SRB history item, if this is an asynchronous request
924 
925 Return Value:
926 
927     none
928 
929 --*/
930 {
931     NTSTATUS                 status  = STATUS_SUCCESS;
932     PSCSI_REQUEST_BLOCK      srb = DeviceExtension->ScratchContext.ScratchSrb;
933     PSRB_HISTORY             history = DeviceExtension->ScratchContext.ScratchHistory;
934     PSRB_HISTORY_ITEM        item = NULL;
935     BOOLEAN                  requestCancelled = FALSE;
936 
937     srb->InternalStatus = 0;
938     srb->SrbStatus = 0;
939 
940     // allocate/update history pre-command, if it is a synchronous request or we were supplied
941     // with a storage for the history item
942     if (SynchronousSrb || SrbHistoryItem != NULL)
943     {
944         // sending a packet implies a new history unit is to be used.
945         NT_ASSERT( history->UsedHistoryCount <= history->TotalHistoryCount );
946 
947         // if already all used up, remove at least one history unit
948         if (history->UsedHistoryCount == history->TotalHistoryCount )
949         {
950             CompressSrbHistoryData(history);
951             NT_ASSERT( history->UsedHistoryCount < history->TotalHistoryCount );
952         }
953 
954         // thus, since we are about to increment the count, it must now be less...
955         NT_ASSERT( history->UsedHistoryCount < history->TotalHistoryCount );
956 
957         // increment the number of history units in use
958         history->UsedHistoryCount++;
959 
960         // determine index to use
961         item = &( history->History[ history->UsedHistoryCount-1 ] );
962 
963         if (SrbHistoryItem != NULL)
964         {
965             *SrbHistoryItem = item;
966         }
967 
968         // zero out the history item
969         RtlZeroMemory(item, sizeof(SRB_HISTORY_ITEM));
970 
971         // Query the tick count and store in the history
972         KeQueryTickCount(&item->TickCountSent);
973     }
974 
975     // get cancellation status;
976     {
977         PCDROM_REQUEST_CONTEXT  requestContext = RequestGetContext(DeviceExtension->ScratchContext.ScratchRequest);
978 
979         if (requestContext->OriginalRequest != NULL)
980         {
981             requestCancelled = WdfRequestIsCanceled(requestContext->OriginalRequest);
982         }
983     }
984 
985     if (!requestCancelled)
986     {
987         status = RequestSend(DeviceExtension,
988                              DeviceExtension->ScratchContext.ScratchRequest,
989                              DeviceExtension->IoTarget,
990                              SynchronousSrb ? WDF_REQUEST_SEND_OPTION_SYNCHRONOUS : 0,
991                              NULL);
992 
993         // If this is a synchronous request, update the history item immediately, including "normalized" sense data
994         if (SynchronousSrb)
995         {
996             ULONG senseSize;
997 
998             // Query the tick count and store in the history
999             KeQueryTickCount(&item->TickCountCompleted);
1000 
1001             // Copy the SRB Status
1002             item->SrbStatus = srb->SrbStatus;
1003 
1004             // Determine the amount of valid sense data
1005             if (srb->SenseInfoBufferLength >= RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength))
1006             {
1007                 PSENSE_DATA sense = (PSENSE_DATA)srb->SenseInfoBuffer;
1008                 senseSize = RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength) +
1009                             sense->AdditionalSenseLength;
1010                 senseSize = min(senseSize, sizeof(SENSE_DATA));
1011             }
1012             else
1013             {
1014                 senseSize = srb->SenseInfoBufferLength;
1015             }
1016 
1017             // Normalize the sense data copy in the history
1018             RtlZeroMemory(&(item->NormalizedSenseData), sizeof(SENSE_DATA));
1019             RtlCopyMemory(&(item->NormalizedSenseData), srb->SenseInfoBuffer, senseSize);
1020         }
1021     }
1022     else
1023     {
1024         DeviceExtension->ScratchContext.ScratchSrb->SrbStatus = SRB_STATUS_ABORTED;
1025         DeviceExtension->ScratchContext.ScratchSrb->InternalStatus = (ULONG)STATUS_CANCELLED;
1026         status = STATUS_CANCELLED;
1027     }
1028 
1029     return status;
1030 }
1031 
1032 VOID
CompressSrbHistoryData(_Inout_ PSRB_HISTORY RequestHistory)1033 CompressSrbHistoryData(
1034     _Inout_  PSRB_HISTORY   RequestHistory
1035     )
1036 /*++
1037 
1038 Routine Description:
1039 
1040     compress the SRB history data.
1041 
1042 Arguments:
1043 
1044     RequestHistory - SRB history data
1045 
1046 Return Value:
1047 
1048     RequestHistory - compressed history data
1049 
1050 --*/
1051 {
1052     ULONG i;
1053     NT_ASSERT( RequestHistory->UsedHistoryCount == RequestHistory->TotalHistoryCount );
1054     ValidateSrbHistoryDataPresumptions(RequestHistory);
1055 
1056     for (i=0; i < RequestHistory->UsedHistoryCount; i++)
1057     {
1058         // for each item...
1059         PSRB_HISTORY_ITEM toMatch = &( RequestHistory->History[i] );
1060         // hint: read const qualifiers backwards.  i.e. srbstatus is a const UCHAR
1061         // so, "UCHAR const * const x" is read "x is a const pointer to a const UCHAR"
1062         // unfortunately, "const UCHAR" is equivalent to "UCHAR const", which causes
1063         // people no end of confusion due to its widespread use.
1064         UCHAR const srbStatus = toMatch->SrbStatus;
1065         UCHAR const sense     = toMatch->NormalizedSenseData.SenseKey;
1066         UCHAR const asc       = toMatch->NormalizedSenseData.AdditionalSenseCode;
1067         UCHAR const ascq      = toMatch->NormalizedSenseData.AdditionalSenseCodeQualifier;
1068         ULONG j;
1069 
1070         // see if there are any at higher indices with identical Sense/ASC/ASCQ
1071         for (j = i+1; (toMatch->ClassDriverUse != 0xFF) && (j < RequestHistory->UsedHistoryCount); j++)
1072         {
1073             PSRB_HISTORY_ITEM found = &( RequestHistory->History[j] );
1074             // close enough match?
1075             if ((srbStatus == found->SrbStatus) &&
1076                 (sense     == found->NormalizedSenseData.SenseKey) &&
1077                 (asc       == found->NormalizedSenseData.AdditionalSenseCode) &&
1078                 (ascq      == found->NormalizedSenseData.AdditionalSenseCodeQualifier)) {
1079 
1080                 // add the fields to keep reasonable track of delay times.
1081                 if (toMatch->MillisecondsDelayOnRetry + found->MillisecondsDelayOnRetry < toMatch->MillisecondsDelayOnRetry) {
1082                     toMatch->MillisecondsDelayOnRetry = MAXULONG;
1083                 } else {
1084                     toMatch->MillisecondsDelayOnRetry += found->MillisecondsDelayOnRetry;
1085                 }
1086 
1087                 // this found item cannot contain any compressed entries because
1088                 // the first entry with a given set of sense/asc/ascq will always
1089                 // either be full (0xFF) or be the only partially-full entry with
1090                 // that sense/asc/ascq.
1091                 NT_ASSERT(found->ClassDriverUse == 0);
1092                 // add the counts so we still know how many retries total
1093                 toMatch->ClassDriverUse++;
1094 
1095 
1096                 // if not the last entry, need to move later entries earlier in the array
1097                 if (j != RequestHistory->UsedHistoryCount-1) {
1098                     // how many entries remain?
1099                     SIZE_T remainingBytes = RequestHistory->UsedHistoryCount - 1 - j;
1100                     remainingBytes *= sizeof(SRB_HISTORY_ITEM);
1101 
1102                     // note that MOVE is required due to overlapping entries
1103                     RtlMoveMemory(found, found+1, remainingBytes);
1104 
1105                     // Finally, decrement the number of used history count and
1106                     // decrement j to rescan the current location again
1107                     --RequestHistory->UsedHistoryCount;
1108                     --j;
1109                 } // end moving of array elements around
1110             } // end of close enough match
1111         } // end j loop
1112     } // end i loop
1113 
1114     // unable to compress duplicate sense/asc/ascq, so just lose the most recent data
1115     if (RequestHistory->UsedHistoryCount == RequestHistory->TotalHistoryCount)
1116     {
1117         PSRB_HISTORY_ITEM item = &( RequestHistory->History[ RequestHistory->TotalHistoryCount-1 ] );
1118         RequestHistory->ClassDriverUse[0] += item->ClassDriverUse; // how many did we "lose"?
1119         RequestHistory->UsedHistoryCount--;
1120     }
1121 
1122     // finally, zero any that are no longer in use
1123     NT_ASSERT( RequestHistory->UsedHistoryCount != RequestHistory->TotalHistoryCount);
1124     {
1125         SIZE_T bytesToZero = RequestHistory->TotalHistoryCount - RequestHistory->UsedHistoryCount;
1126         bytesToZero *= sizeof(SRB_HISTORY_ITEM);
1127         RtlZeroMemory(&(RequestHistory->History[RequestHistory->UsedHistoryCount]), bytesToZero);
1128     }
1129 
1130     ValidateSrbHistoryDataPresumptions(RequestHistory);
1131     return;
1132 }
1133 
1134 VOID
ValidateSrbHistoryDataPresumptions(_In_ SRB_HISTORY const * RequestHistory)1135 ValidateSrbHistoryDataPresumptions(
1136     _In_     SRB_HISTORY const * RequestHistory
1137     )
1138 {
1139 #if DBG
1140     // validate that all fully-compressed items are before any non-fully-compressed items of any particular sense/asc/ascq
1141     // validate that there is at most one partially-compressed item of any particular sense/asc/ascq
1142     // validate that all items of any particular sense/asc/ascq that are uncompressed are at the end
1143     // THUS: A(255) A(255) A( 40) A(  0) A(  0) is legal for all types with A as sense/asc/ascq
1144     //       A(0)   B(255) A(  0) B( 17) B(  0) is also legal because A/B are different types of error
1145 
1146     ULONG i;
1147     for (i = 0; i < RequestHistory->UsedHistoryCount; i++)
1148     {
1149         SRB_HISTORY_ITEM const * toMatch = &( RequestHistory->History[i] );
1150         UCHAR const srbStatus = toMatch->SrbStatus;
1151         UCHAR const sense     = toMatch->NormalizedSenseData.SenseKey;
1152         UCHAR const asc       = toMatch->NormalizedSenseData.AdditionalSenseCode;
1153         UCHAR const ascq      = toMatch->NormalizedSenseData.AdditionalSenseCodeQualifier;
1154         ULONG j;
1155 
1156         BOOLEAN foundPartiallyCompressedItem =
1157             (toMatch->ClassDriverUse !=    0) &&
1158             (toMatch->ClassDriverUse != 0xFF) ;
1159         BOOLEAN foundUncompressedItem =
1160             (toMatch->ClassDriverUse ==    0) ;
1161 
1162         for (j = i+1; j < RequestHistory->UsedHistoryCount; j++)
1163         {
1164             SRB_HISTORY_ITEM const * found = &( RequestHistory->History[j] );
1165             if ((srbStatus == found->SrbStatus) &&
1166                 (sense     == found->NormalizedSenseData.SenseKey) &&
1167                 (asc       == found->NormalizedSenseData.AdditionalSenseCode) &&
1168                 (ascq      == found->NormalizedSenseData.AdditionalSenseCodeQualifier)
1169                 )
1170             {
1171                 // found a matching type, so validate ordering rules
1172                 if (foundUncompressedItem && (found->ClassDriverUse != 0))
1173                 {
1174                     DbgPrintEx(DPFLTR_CDROM_ID, DPFLTR_ERROR_LEVEL,
1175                                "History data has compressed history following uncompressed history "
1176                                "for srbstatus/sense/asc/ascq of %02x/%02x/%02x/%02x at indices %d (%08x) and %d (%08x)\n",
1177                                srbStatus, sense, asc, ascq,
1178                                i,i, j,j
1179                                );
1180                     NT_ASSERT(FALSE);
1181                 }
1182                 else if (foundPartiallyCompressedItem && (found->ClassDriverUse == 0xFF))
1183                 {
1184                     DbgPrintEx(DPFLTR_CDROM_ID, DPFLTR_ERROR_LEVEL,
1185                                "History data has fully compressed history following partially compressed history "
1186                                "for srbstatus/sense/asc/ascq of %02x/%02x/%02x/%02x at indices %d (%08x) and %d (%08x)\n",
1187                                srbStatus, sense, asc, ascq,
1188                                i,i, j,j
1189                                );
1190                     NT_ASSERT(FALSE);
1191                 }
1192 
1193                 // update if we have now found partially compressed and/or uncompressed items
1194                 if (found->ClassDriverUse == 0)
1195                 {
1196                     foundUncompressedItem = TRUE;
1197                 }
1198                 else if (found->ClassDriverUse != 0xFF)
1199                 {
1200                     foundPartiallyCompressedItem = TRUE;
1201                 }
1202             } // end match of (toMatch,found)
1203         } // end loop j
1204     } // end loop i
1205 #else
1206     UNREFERENCED_PARAMETER(RequestHistory);
1207 #endif
1208     return;
1209 }
1210 
1211 VOID
ScratchBuffer_SetupReadWriteSrb(_Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension,_In_ WDFREQUEST OriginalRequest,_In_ LARGE_INTEGER StartingOffset,_In_ ULONG RequiredLength,_Inout_updates_bytes_ (RequiredLength)UCHAR * DataBuffer,_In_ BOOLEAN IsReadRequest,_In_ BOOLEAN UsePartialMdl)1212 ScratchBuffer_SetupReadWriteSrb(
1213     _Inout_ PCDROM_DEVICE_EXTENSION     DeviceExtension,
1214     _In_    WDFREQUEST                  OriginalRequest,
1215     _In_    LARGE_INTEGER               StartingOffset,
1216     _In_    ULONG                       RequiredLength,
1217     _Inout_updates_bytes_(RequiredLength) UCHAR* DataBuffer,
1218     _In_    BOOLEAN                     IsReadRequest,
1219     _In_    BOOLEAN                     UsePartialMdl
1220     )
1221 /*++
1222 
1223 Routine Description:
1224 
1225     setup SRB for read/write request.
1226 
1227 Arguments:
1228 
1229     DeviceExtension - device extension
1230     OriginalRequest - read/write request
1231     StartingOffset - read/write starting offset
1232     DataBuffer - buffer for read/write
1233     IsReadRequest - TRUE (read); FALSE (write)
1234 
1235 Return Value:
1236 
1237     none
1238 
1239 --*/
1240 {
1241     //NOTE: R/W request not use the ScratchBuffer, instead, it uses the buffer associated with IRP.
1242 
1243     PSCSI_REQUEST_BLOCK srb = DeviceExtension->ScratchContext.ScratchSrb;
1244     PCDB                cdb = (PCDB)srb->Cdb;
1245     LARGE_INTEGER       logicalBlockAddr;
1246     ULONG               numTransferBlocks;
1247 
1248     PIRP                originalIrp = WdfRequestWdmGetIrp(OriginalRequest);
1249 
1250     PIRP                irp = WdfRequestWdmGetIrp(DeviceExtension->ScratchContext.ScratchRequest);
1251     PIO_STACK_LOCATION  irpStack = NULL;
1252 
1253     PCDROM_REQUEST_CONTEXT  requestContext = RequestGetContext(DeviceExtension->ScratchContext.ScratchRequest);
1254 
1255     requestContext->OriginalRequest = OriginalRequest;
1256 
1257 
1258     logicalBlockAddr.QuadPart = Int64ShrlMod32(StartingOffset.QuadPart, DeviceExtension->SectorShift);
1259     numTransferBlocks = RequiredLength >> DeviceExtension->SectorShift;
1260 
1261     // set to use the full scratch buffer via the scratch SRB
1262     irpStack = IoGetNextIrpStackLocation(irp);
1263     irpStack->MajorFunction = IRP_MJ_SCSI;
1264     if (IsReadRequest)
1265     {
1266         irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN;
1267     }
1268     else
1269     {
1270         irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_OUT;
1271     }
1272     irpStack->Parameters.Scsi.Srb = srb;
1273 
1274     // prepare the SRB with default values
1275     srb->Length = SCSI_REQUEST_BLOCK_SIZE;
1276     srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
1277     srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
1278     srb->SrbStatus = 0;
1279     srb->ScsiStatus = 0;
1280     srb->NextSrb = NULL;
1281     srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
1282     srb->SenseInfoBuffer = DeviceExtension->ScratchContext.ScratchSense;
1283 
1284     srb->DataBuffer = DataBuffer;
1285     srb->DataTransferLength = RequiredLength;
1286 
1287     srb->QueueSortKey = logicalBlockAddr.LowPart;
1288     if (logicalBlockAddr.QuadPart > 0xFFFFFFFF)
1289     {
1290         //
1291         // If the requested LBA is more than max ULONG set the
1292         // QueueSortKey to the maximum value, so that these
1293         // requests can be added towards the end of the queue.
1294         //
1295         srb->QueueSortKey = 0xFFFFFFFF;
1296     }
1297 
1298     srb->OriginalRequest = irp;
1299     srb->TimeOutValue = DeviceExtension->TimeOutValue;
1300 
1301     if (RequestIsRealtimeStreaming(OriginalRequest, IsReadRequest) &&
1302         !TEST_FLAG(DeviceExtension->PrivateFdoData->HackFlags, FDO_HACK_NO_STREAMING))
1303     {
1304         if (IsReadRequest)
1305         {
1306             RtlZeroMemory(&cdb->READ12, sizeof(cdb->READ12));
1307             REVERSE_BYTES(&cdb->READ12.LogicalBlock, &logicalBlockAddr.LowPart);
1308             REVERSE_BYTES(&cdb->READ12.TransferLength, &numTransferBlocks);
1309             cdb->READ12.Streaming = 1;
1310             cdb->READ12.OperationCode = SCSIOP_READ12;
1311             srb->CdbLength = sizeof(cdb->READ12);
1312         }
1313         else
1314         {
1315             RtlZeroMemory(&cdb->WRITE12, sizeof(cdb->WRITE12));
1316             REVERSE_BYTES(&cdb->WRITE12.LogicalBlock, &logicalBlockAddr.LowPart);
1317             REVERSE_BYTES(&cdb->WRITE12.TransferLength, &numTransferBlocks);
1318             cdb->WRITE12.Streaming = 1;
1319             cdb->WRITE12.OperationCode = SCSIOP_WRITE12;
1320             srb->CdbLength = sizeof(cdb->WRITE12);
1321         }
1322     }
1323     else
1324     {
1325         RtlZeroMemory(&cdb->CDB10, sizeof(cdb->CDB10));
1326         cdb->CDB10.LogicalBlockByte0 = ((PFOUR_BYTE)&logicalBlockAddr.LowPart)->Byte3;
1327         cdb->CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)&logicalBlockAddr.LowPart)->Byte2;
1328         cdb->CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)&logicalBlockAddr.LowPart)->Byte1;
1329         cdb->CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)&logicalBlockAddr.LowPart)->Byte0;
1330         cdb->CDB10.TransferBlocksMsb = ((PFOUR_BYTE)&numTransferBlocks)->Byte1;
1331         cdb->CDB10.TransferBlocksLsb = ((PFOUR_BYTE)&numTransferBlocks)->Byte0;
1332         cdb->CDB10.OperationCode = (IsReadRequest) ? SCSIOP_READ : SCSIOP_WRITE;
1333         srb->CdbLength = sizeof(cdb->CDB10);
1334     }
1335 
1336     //  Set SRB and IRP flags
1337     srb->SrbFlags = DeviceExtension->SrbFlags;
1338     if (TEST_FLAG(originalIrp->Flags, IRP_PAGING_IO) ||
1339         TEST_FLAG(originalIrp->Flags, IRP_SYNCHRONOUS_PAGING_IO))
1340     {
1341         SET_FLAG(srb->SrbFlags, SRB_CLASS_FLAGS_PAGING);
1342     }
1343 
1344     SET_FLAG(srb->SrbFlags, (IsReadRequest) ? SRB_FLAGS_DATA_IN : SRB_FLAGS_DATA_OUT);
1345     SET_FLAG(srb->SrbFlags, SRB_FLAGS_ADAPTER_CACHE_ENABLE);
1346 
1347     //
1348     // If the request is not split, we can use the original IRP MDL.  If the
1349     // request needs to be split, we need to use a partial MDL.  The partial MDL
1350     // is needed because more than one driver might be mapping the same MDL
1351     // and this causes problems.
1352     //
1353     if (UsePartialMdl == FALSE)
1354     {
1355         irp->MdlAddress = originalIrp->MdlAddress;
1356     }
1357     else
1358     {
1359         if (DeviceExtension->ScratchContext.PartialMdlIsBuilt != FALSE)
1360         {
1361             MmPrepareMdlForReuse(DeviceExtension->ScratchContext.PartialMdl);
1362         }
1363 
1364         IoBuildPartialMdl(originalIrp->MdlAddress, DeviceExtension->ScratchContext.PartialMdl, srb->DataBuffer, srb->DataTransferLength);
1365         DeviceExtension->ScratchContext.PartialMdlIsBuilt = TRUE;
1366         irp->MdlAddress = DeviceExtension->ScratchContext.PartialMdl;
1367     }
1368 
1369     //DBGLOGSENDPACKET(Pkt);
1370     //HISTORYLOGSENDPACKET(Pkt);
1371 
1372     //
1373     // Set the original irp here for SFIO.
1374     //
1375     srb->SrbExtension = (PVOID)(originalIrp);
1376 
1377     return;
1378 }
1379 
_IRQL_requires_max_(APC_LEVEL)1380 _IRQL_requires_max_(APC_LEVEL)
1381 NTSTATUS
1382 ScratchBuffer_ExecuteCdbEx(
1383     _Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension,
1384     _In_opt_ WDFREQUEST             OriginalRequest,
1385     _In_ ULONG                      TransferSize,
1386     _In_ BOOLEAN                    GetDataFromDevice,
1387     _In_ PCDB                       Cdb,
1388     _In_ UCHAR                      OprationLength,
1389     _In_ ULONG                      TimeoutValue
1390     )
1391 /*++
1392 
1393 Routine Description:
1394 
1395     Use Scratch buffer to send the Cdb, check error and retry if necessary.
1396 
1397 Arguments:
1398 
1399     DeviceExtension - device context
1400     OriginalRequest - original request that requires this CDB operation
1401     TransferSize - Data transfer size required
1402     GetFromDevice - TRUE if getting data from device.
1403     Cdb - SCSI command
1404     OprationLength - SCSI command length: 6, 10 or 12
1405     TimeoutValue - if > 0, use it as timeout value for command
1406                    if 0, use the default device timeout value
1407 
1408 Return Value:
1409 
1410     NTSTATUS
1411 
1412 --*/
1413 {
1414     NTSTATUS            status = STATUS_SUCCESS;
1415     PSCSI_REQUEST_BLOCK srb = DeviceExtension->ScratchContext.ScratchSrb;
1416     PCDB                cdb = (PCDB)(srb->Cdb);
1417 
1418     BOOLEAN             shouldRetry = TRUE;
1419     ULONG               timesAlreadyRetried = 0;
1420     LONGLONG            retryIn100nsUnits = 0;
1421 
1422     PAGED_CODE ();
1423 
1424     while (shouldRetry)
1425     {
1426         ScratchBuffer_SetupSrb(DeviceExtension, OriginalRequest, TransferSize, GetDataFromDevice);
1427 
1428         // Set up the SRB/CDB
1429         RtlCopyMemory(cdb, Cdb, sizeof(CDB));
1430 
1431         srb->CdbLength = OprationLength;
1432 
1433         if (TimeoutValue > 0)
1434         {
1435             srb->TimeOutValue = TimeoutValue;
1436         }
1437 
1438         ScratchBuffer_SendSrb(DeviceExtension, TRUE, NULL);
1439 
1440         if ((DeviceExtension->ScratchContext.ScratchSrb->SrbStatus == SRB_STATUS_ABORTED) &&
1441             (DeviceExtension->ScratchContext.ScratchSrb->InternalStatus == STATUS_CANCELLED))
1442         {
1443             shouldRetry = FALSE;
1444             status = STATUS_CANCELLED;
1445         }
1446         else
1447         {
1448             shouldRetry = RequestSenseInfoInterpretForScratchBuffer(DeviceExtension,
1449                                                                     timesAlreadyRetried,
1450                                                                     &status,
1451                                                                     &retryIn100nsUnits);
1452             if (shouldRetry)
1453             {
1454                 LARGE_INTEGER t;
1455                 t.QuadPart = -retryIn100nsUnits;
1456                 timesAlreadyRetried++;
1457                 KeDelayExecutionThread(KernelMode, FALSE, &t);
1458                 // keep items clean
1459                 ScratchBuffer_ResetItems(DeviceExtension, FALSE);
1460             }
1461         }
1462     }
1463 
1464     return status;
1465 }
1466 
1467 
1468