[IMM32] Improve ImmGetImeInfoEx (#3833)
[reactos.git] / drivers / storage / class / cdrom_new / scratch.c
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
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
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
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
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 */
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 */
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 */
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
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
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
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
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
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