ReactOS 0.4.15-dev-7961-gdcf9eb0
scratch.c
Go to the documentation of this file.
1/*--
2
3Copyright (C) Microsoft Corporation. All rights reserved.
4
5Module Name:
6
7 scratch.c
8
9Abstract:
10
11 Functions for using common scratch buffer
12
13Environment:
14
15 kernel mode only
16
17Notes:
18
19
20Revision 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
39EVT_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
51VOID
52ScratchBuffer_Deallocate(
53 _Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension
54 )
55/*++
56
57Routine Description:
58
59 release all resources allocated for scratch.
60
61Arguments:
62
63 DeviceExtension - device extension
64
65Return 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
127ScratchBuffer_Allocate(
128 _Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension
129 )
130/*++
131
132Routine Description:
133
134 allocate resources allocated for scratch.
135
136Arguments:
137
138 DeviceExtension - device extension
139
140Return Value:
141
142 none
143
144--*/
145{
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,
184 if (DeviceExtension->ScratchContext.ScratchBuffer == NULL)
185 {
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 {
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 {
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;
229
230 status = WdfRequestCreate(&attributes,
231 DeviceExtension->IoTarget,
232 &DeviceExtension->ScratchContext.ScratchRequest);
233
234 if ((!NT_SUCCESS(status)) ||
235 (DeviceExtension->ScratchContext.ScratchRequest == NULL))
236 {
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),
249
250 if (DeviceExtension->ScratchContext.ScratchSrb == NULL)
251 {
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),
264
265 if (DeviceExtension->ScratchContext.ScratchSense == NULL)
266 {
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,
283 if (DeviceExtension->ScratchContext.ScratchHistory == NULL)
284 {
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 {
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 {
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
342VOID
344 _Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension,
345 _In_ BOOLEAN ResetRequestHistory
346 )
347/*++
348
349Routine Description:
350
351 reset scratch items for reuse.
352
353Arguments:
354
355 DeviceExtension - device extension
356 ResetRequestHistory - reset history fields or not
357
358Return Value:
359
360 none
361
362--*/
363{
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
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,
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
430 _In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
432 )
433/*++
434
435Routine Description:
436
437 This function asynchronously sends the next read/write SRB down the stack.
438
439Arguments:
440
441 DeviceExtension - Device extension
442
443Return 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;
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,
479
480 status = ScratchBuffer_SendSrb(DeviceExtension, FALSE, (FirstTry ? &readWriteContext->SrbHistoryItem : NULL));
481
482 return status;
483}
484
485
486VOID
487NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
489 struct _KDPC *Dpc,
493 )
494/*++
495
496Routine Description:
497
498 Timer routine for retrying read and write requests.
499
500Arguments:
501
502 Timer - WDF timer
503
504Return 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;
515 KIRQL oldIrql;
516
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 {
542 }
543
544 KeReleaseSpinLock(&requestContext->ReadWriteCancelSpinLock, oldIrql);
545
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
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
569
570VOID
571NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
573 _In_ WDFREQUEST Request
574 )
575/*++
576
577Routine 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
582Arguments:
583
584 Request - WDF request
585
586Return Value:
587
588 none
589
590--*/
591{
592 PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(Request);
593 PCDROM_DEVICE_EXTENSION deviceExtension = requestContext->DeviceExtension;
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 //
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
626VOID
627NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
629 _In_ WDFREQUEST Request,
630 _In_ WDFIOTARGET Target,
633 )
634/*++
635
636Routine Description:
637
638 Read/write request completion routine.
639
640Arguments:
641 Request - WDF request
642 Target - The IO target the request was completed by.
643 Params - the request completion parameters
644 Context - context
645
646Return Value:
647
648 none
649
650--*/
651{
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
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) &&
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.
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
725 {
726 requestContext->ReadWriteIsCompleted = TRUE;
727
728 KeReleaseSpinLock(&requestContext->ReadWriteCancelSpinLock, oldIrql);
729 }
730 else
731 {
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
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 {
775 senseSize = RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, 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));
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 {
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
810VOID
811ScratchBuffer_SetupSrb(
812 _Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension,
813 _In_opt_ WDFREQUEST OriginalRequest,
816 )
817/*++
818
819Routine Description:
820
821 setup scratch SRB for sending out.
822
823Arguments:
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
830Return 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
875 srb->SrbStatus = 0;
876 srb->ScsiStatus = 0;
877 srb->NextSrb = NULL;
878 srb->OriginalRequest = irp;
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;
889
890 if (MaximumTransferLength == 0)
891 {
893 }
894 else if (GetDataFromDevice)
895 {
897 }
898 else
899 {
901 }
902}
903
904
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
915Routine Description:
916
917 Send the command from the scratch SRB to lower driver and retry if necessary.
918
919Arguments:
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
925Return Value:
926
927 none
928
929--*/
930{
932 PSCSI_REQUEST_BLOCK srb = DeviceExtension->ScratchContext.ScratchSrb;
933 PSRB_HISTORY history = DeviceExtension->ScratchContext.ScratchHistory;
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
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 {
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;
1027 }
1028
1029 return status;
1030}
1031
1032VOID
1035 )
1036/*++
1037
1038Routine Description:
1039
1040 compress the SRB history data.
1041
1042Arguments:
1043
1044 RequestHistory - SRB history data
1045
1046Return Value:
1047
1048 RequestHistory - compressed history data
1049
1050--*/
1051{
1052 ULONG i;
1053 NT_ASSERT( RequestHistory->UsedHistoryCount == RequestHistory->TotalHistoryCount );
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;
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) &&
1079
1080 // add the fields to keep reasonable track of delay times.
1083 } else {
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
1131 return;
1132}
1133
1134VOID
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;
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) &&
1169 )
1170 {
1171 // found a matching type, so validate ordering rules
1172 if (foundUncompressedItem && (found->ClassDriverUse != 0))
1173 {
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 );
1181 }
1182 else if (foundPartiallyCompressedItem && (found->ClassDriverUse == 0xFF))
1183 {
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 );
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
1207#endif
1208 return;
1209}
1210
1211VOID
1213 _Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension,
1214 _In_ WDFREQUEST OriginalRequest,
1218 _In_ BOOLEAN IsReadRequest,
1219 _In_ BOOLEAN UsePartialMdl
1220 )
1221/*++
1222
1223Routine Description:
1224
1225 setup SRB for read/write request.
1226
1227Arguments:
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
1235Return 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
1278 srb->SrbStatus = 0;
1279 srb->ScsiStatus = 0;
1280 srb->NextSrb = NULL;
1282 srb->SenseInfoBuffer = DeviceExtension->ScratchContext.ScratchSense;
1283
1284 srb->DataBuffer = DataBuffer;
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 {
1342 }
1343
1344 SET_FLAG(srb->SrbFlags, (IsReadRequest) ? SRB_FLAGS_DATA_IN : SRB_FLAGS_DATA_OUT);
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
1382ScratchBuffer_ExecuteCdbEx(
1383 _Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension,
1384 _In_opt_ WDFREQUEST OriginalRequest,
1387 _In_ PCDB Cdb,
1390 )
1391/*++
1392
1393Routine Description:
1394
1395 Use Scratch buffer to send the Cdb, check error and retry if necessary.
1396
1397Arguments:
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
1408Return Value:
1409
1410 NTSTATUS
1411
1412--*/
1413{
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
1432
1433 if (TimeoutValue > 0)
1434 {
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;
1445 }
1446 else
1447 {
1448 shouldRetry = RequestSenseInfoInterpretForScratchBuffer(DeviceExtension,
1449 timesAlreadyRetried,
1450 &status,
1451 &retryIn100nsUnits);
1452 if (shouldRetry)
1453 {
1455 t.QuadPart = -retryIn100nsUnits;
1456 timesAlreadyRetried++;
1458 // keep items clean
1459 ScratchBuffer_ResetItems(DeviceExtension, FALSE);
1460 }
1461 }
1462 }
1463
1464 return status;
1465}
1466
1467
#define PAGED_CODE()
unsigned char BOOLEAN
#define DPFLTR_ERROR_LEVEL
Definition: main.cpp:32
LONG NTSTATUS
Definition: precomp.h:26
_In_ PFCB _In_ LONGLONG StartingOffset
Definition: cdprocs.h:291
struct _CDROM_DEVICE_EXTENSION * PCDROM_DEVICE_EXTENSION
Definition: cdrom.h:218
BOOLEAN RequestSenseInfoInterpretForScratchBuffer(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ ULONG RetriedCount, _Out_ NTSTATUS *Status, _Out_ _Deref_out_range_(0, MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS) LONGLONG *RetryIntervalIn100ns)
Definition: sense.c:2564
#define CDROM_TAG_SCRATCH
Definition: cdrom.h:727
_In_ PSCSI_REQUEST_BLOCK _In_opt_ PVOID _In_ ULONG _In_ BOOLEAN _In_opt_ WDFREQUEST OriginalRequest
Definition: cdrom.h:994
NTSTATUS RequestSend(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ WDFIOTARGET IoTarget, _In_ ULONG Flags, _Out_opt_ PBOOLEAN RequestSent)
Definition: common.c:3793
VOID RequestCompletion(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ WDFREQUEST Request, _In_ NTSTATUS Status, _In_ ULONG_PTR Information)
Definition: common.c:3439
#define SRB_CLASS_FLAGS_PAGING
Definition: cdrom.h:165
#define TEST_FLAG(Flags, Bit)
Definition: cdrom.h:1495
#define SET_FLAG(Flags, Bit)
Definition: cdrom.h:1493
#define FDO_HACK_NO_STREAMING
Definition: cdromp.h:137
struct _SRB_HISTORY SRB_HISTORY
struct _SRB_HISTORY_ITEM SRB_HISTORY_ITEM
#define IOCTL_SCSI_EXECUTE_IN
Definition: cdrw_hw.h:1451
struct _SENSE_DATA * PSENSE_DATA
#define SCSIOP_WRITE
Definition: cdrw_hw.h:906
#define IOCTL_SCSI_EXECUTE_OUT
Definition: cdrw_hw.h:1452
#define IOCTL_SCSI_EXECUTE_NONE
Definition: cdrw_hw.h:1453
#define SCSIOP_READ12
Definition: cdrw_hw.h:956
#define SENSE_BUFFER_SIZE
Definition: cdrw_hw.h:1183
#define SCSIOP_WRITE12
Definition: cdrw_hw.h:957
union _CDB * PCDB
#define SCSIOP_READ
Definition: cdrw_hw.h:905
_In_opt_ PIRP _In_ PSCSI_REQUEST_BLOCK _In_ UCHAR _In_ ULONG _In_ ULONG _In_opt_ SRB_HISTORY * RequestHistory
Definition: classpnp.h:487
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
#define NT_SUCCESS(StatCode)
Definition: apphelp.c:32
VOID NTAPI KeInitializeDpc(IN PKDPC Dpc, IN PKDEFERRED_ROUTINE DeferredRoutine, IN PVOID DeferredContext)
Definition: dpc.c:712
@ DPFLTR_CDROM_ID
Definition: dpfilter.h:35
BOOLEAN RequestIsRealtimeStreaming(_In_ WDFREQUEST Request, _In_ BOOLEAN IsReadRequest)
Definition: ioctl.c:6050
#define SCSI_REQUEST_BLOCK_SIZE
Definition: srb.h:282
#define SRB_FUNCTION_EXECUTE_SCSI
Definition: srb.h:315
#define SRB_FLAGS_DATA_OUT
Definition: srb.h:401
#define SRB_STATUS_ABORTED
Definition: srb.h:342
#define SRB_FLAGS_NO_DATA_TRANSFER
Definition: srb.h:402
#define SRB_SIMPLE_TAG_REQUEST
Definition: srb.h:423
#define SRB_FLAGS_DATA_IN
Definition: srb.h:400
#define SRB_FLAGS_ADAPTER_CACHE_ENABLE
Definition: srb.h:405
#define SRB_FLAGS_DISABLE_SYNCH_TRANSFER
Definition: srb.h:397
#define SRB_STATUS(Status)
Definition: srb.h:389
#define SRB_FLAGS_NO_QUEUE_FREEZE
Definition: srb.h:404
#define SRB_STATUS_SUCCESS
Definition: srb.h:341
#define _IRQL_requires_max_(irql)
Definition: driverspecs.h:230
#define ExAllocatePoolWithTag(hernya, size, tag)
Definition: env_spec_w32.h:350
UCHAR KIRQL
Definition: env_spec_w32.h:591
#define KeReleaseSpinLock(sl, irql)
Definition: env_spec_w32.h:627
#define APC_LEVEL
Definition: env_spec_w32.h:695
#define PAGE_SIZE
Definition: env_spec_w32.h:49
#define KeAcquireSpinLock(sl, irql)
Definition: env_spec_w32.h:609
#define ExFreePool(addr)
Definition: env_spec_w32.h:352
#define KeDelayExecutionThread(mode, foo, t)
Definition: env_spec_w32.h:484
#define KeInitializeSpinLock(sl)
Definition: env_spec_w32.h:604
struct _FOUR_BYTE * PFOUR_BYTE
#define IoFreeMdl
Definition: fxmdl.h:89
#define IoAllocateMdl
Definition: fxmdl.h:88
FxIrp * irp
GLdouble GLdouble t
Definition: gl.h:2047
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint i
Definition: glfuncs.h:248
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint GLint GLint j
Definition: glfuncs.h:250
VOID NTAPI IoBuildPartialMdl(IN PMDL SourceMdl, IN PMDL TargetMdl, IN PVOID VirtualAddress, IN ULONG Length)
Definition: iomdl.c:96
VOID NTAPI MmBuildMdlForNonPagedPool(IN PMDL Mdl)
Definition: mdlsup.c:424
static ATOM item
Definition: dde.c:856
#define min(a, b)
Definition: monoChain.cc:55
#define _Inout_
Definition: ms_sal.h:378
#define _Pre_null_
Definition: ms_sal.h:682
#define _Inout_updates_bytes_(size)
Definition: ms_sal.h:399
#define _When_(expr, annos)
Definition: ms_sal.h:254
#define _In_
Definition: ms_sal.h:308
#define _In_opt_
Definition: ms_sal.h:309
#define KernelMode
Definition: asm.h:34
NTSYSAPI ULONG __cdecl DbgPrintEx(_In_ ULONG ComponentId, _In_ ULONG Level, _In_z_ _Printf_format_string_ PCSTR Format,...)
#define Int64ShrlMod32(a, b)
#define RTL_SIZEOF_THROUGH_FIELD(type, field)
Definition: ntbasedef.h:672
#define UNREFERENCED_PARAMETER(P)
Definition: ntbasedef.h:317
@ FirstTry
Definition: copy.c:25
#define STATUS_INTERNAL_ERROR
Definition: ntstatus.h:465
#define STATUS_NOT_SUPPORTED
Definition: ntstatus.h:423
#define STATUS_INTEGER_OVERFLOW
Definition: ntstatus.h:385
EVT_WDF_REQUEST_COMPLETION_ROUTINE ScratchBuffer_ReadWriteCompletionRoutine
Definition: scratch.c:39
VOID ScratchBuffer_ResetItems(_Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ BOOLEAN ResetRequestHistory)
Definition: scratch.c:343
EVT_WDF_REQUEST_CANCEL ScratchBuffer_ReadWriteEvtRequestCancel
Definition: scratch.c:568
NTSTATUS ScratchBuffer_SendSrb(_Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ BOOLEAN SynchronousSrb, _When_(SynchronousSrb, _Pre_null_) _When_(!SynchronousSrb, _In_opt_) PSRB_HISTORY_ITEM *SrbHistoryItem)
Definition: scratch.c:906
VOID ValidateSrbHistoryDataPresumptions(_In_ SRB_HISTORY const *RequestHistory)
Definition: scratch.c:1135
NTSTATUS ScratchBuffer_PerformNextReadWrite(_In_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_ BOOLEAN FirstTry)
Definition: scratch.c:429
VOID CompressSrbHistoryData(_Inout_ PSRB_HISTORY RequestHistory)
Definition: scratch.c:1033
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)
Definition: scratch.c:1212
_In_opt_ WDFREQUEST _In_ ULONG _In_ BOOLEAN _In_ PCDB _In_ UCHAR _In_ ULONG TimeoutValue
Definition: scratch.h:162
FORCEINLINE VOID ScratchBuffer_EndUse(_Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension)
Definition: scratch.h:104
_In_opt_ WDFREQUEST _In_ ULONG _In_ BOOLEAN _In_ PCDB Cdb
Definition: scratch.h:159
_In_opt_ WDFREQUEST _In_ ULONG MaximumTransferLength
Definition: scratch.h:54
_In_opt_ WDFREQUEST _In_ ULONG TransferSize
Definition: scratch.h:157
_In_opt_ WDFREQUEST _In_ ULONG _In_ BOOLEAN _In_ PCDB _In_ UCHAR OprationLength
Definition: scratch.h:160
KDEFERRED_ROUTINE ScratchBuffer_ReadWriteTimerRoutine
Definition: scratch.h:185
_In_opt_ WDFREQUEST _In_ ULONG _In_ BOOLEAN GetDataFromDevice
Definition: scratch.h:56
#define REVERSE_BYTES(Destination, Source)
Definition: scsi.h:3465
#define KeQueryTickCount(CurrentCount)
Definition: ke.h:43
ULONG PFN_NUMBER
Definition: ke.h:9
#define STATUS_SUCCESS
Definition: shellext.h:65
#define TRACE_LEVEL_WARNING
Definition: storswtr.h:28
#define TRACE_LEVEL_FATAL
Definition: storswtr.h:26
#define TRACE_LEVEL_ERROR
Definition: storswtr.h:27
CDROM_SCRATCH_CONTEXT ScratchContext
Definition: cdrom.h:601
WDFREQUEST OriginalRequest
Definition: cdrom.h:633
PCDROM_DEVICE_EXTENSION DeviceExtension
Definition: cdrom.h:631
KSPIN_LOCK ReadWriteCancelSpinLock
Definition: cdrom.h:662
BOOLEAN ReadWriteIsCompleted
Definition: cdrom.h:665
KTIMER ReadWriteTimer
Definition: cdrom.h:663
BOOLEAN ReadWriteRetryInitialized
Definition: cdrom.h:666
PSCSI_REQUEST_BLOCK ScratchSrb
Definition: cdrom.h:304
CDROM_SCRATCH_READ_WRITE_CONTEXT ScratchReadWriteContext
Definition: cdrom.h:321
WDFREQUEST ScratchRequest
Definition: cdrom.h:303
PSRB_HISTORY_ITEM SrbHistoryItem
Definition: cdrom.h:289
LARGE_INTEGER StartingOffset
Definition: cdrom.h:285
struct _IO_STACK_LOCATION::@1564::@1565 DeviceIoControl
union _IO_STACK_LOCATION::@1564 Parameters
struct _IO_STACK_LOCATION::@3978::@4000 Scsi
Definition: ketypes.h:699
ULONG TimeOutValue
Definition: srb.h:262
PVOID OriginalRequest
Definition: srb.h:266
UCHAR SenseInfoBufferLength
Definition: srb.h:259
PVOID DataBuffer
Definition: srb.h:263
UCHAR QueueAction
Definition: srb.h:257
UCHAR CdbLength
Definition: srb.h:258
UCHAR Cdb[16]
Definition: srb.h:279
PVOID SenseInfoBuffer
Definition: srb.h:264
ULONG QueueSortKey
Definition: srb.h:270
UCHAR Function
Definition: srb.h:250
UCHAR ScsiStatus
Definition: srb.h:252
ULONG DataTransferLength
Definition: srb.h:261
PVOID SrbExtension
Definition: srb.h:267
ULONG InternalStatus
Definition: srb.h:269
struct _SCSI_REQUEST_BLOCK * NextSrb
Definition: srb.h:265
ULONG SrbFlags
Definition: srb.h:260
USHORT Length
Definition: srb.h:249
UCHAR SrbStatus
Definition: srb.h:251
UCHAR AdditionalSenseLength
Definition: cdrw_hw.h:1173
UCHAR AdditionalSenseCode
Definition: cdrw_hw.h:1175
UCHAR AdditionalSenseCodeQualifier
Definition: cdrw_hw.h:1176
UCHAR SenseKey
Definition: cdrw_hw.h:1167
LARGE_INTEGER TickCountCompleted
Definition: cdromp.h:98
UCHAR ClassDriverUse
Definition: cdromp.h:102
ULONG MillisecondsDelayOnRetry
Definition: cdromp.h:99
UCHAR SrbStatus
Definition: cdromp.h:101
SENSE_DATA NormalizedSenseData
Definition: cdromp.h:100
ULONG_PTR ClassDriverUse[4]
Definition: cdromp.h:106
Definition: tftpd.h:86
Definition: ps.c:97
BOOLEAN NTAPI KeSetTimer(IN OUT PKTIMER Timer, IN LARGE_INTEGER DueTime, IN PKDPC Dpc OPTIONAL)
Definition: timerobj.c:281
BOOLEAN NTAPI KeCancelTimer(IN OUT PKTIMER Timer)
Definition: timerobj.c:206
VOID NTAPI KeInitializeTimer(OUT PKTIMER Timer)
Definition: timerobj.c:233
#define MAXULONG
Definition: typedefs.h:251
int64_t LONGLONG
Definition: typedefs.h:68
#define NTAPI
Definition: typedefs.h:36
void * PVOID
Definition: typedefs.h:50
ULONG_PTR SIZE_T
Definition: typedefs.h:80
#define RtlCopyMemory(Destination, Source, Length)
Definition: typedefs.h:263
#define RtlZeroMemory(Destination, Length)
Definition: typedefs.h:262
#define RtlMoveMemory(Destination, Source, Length)
Definition: typedefs.h:264
uint32_t ULONG
Definition: typedefs.h:59
#define STATUS_INSUFFICIENT_RESOURCES
Definition: udferr_usr.h:158
#define STATUS_CANCELLED
Definition: udferr_usr.h:170
short CSHORT
Definition: umtypes.h:127
Definition: cdrw_hw.h:28
struct _CDB::_CDB10 CDB10
struct _CDB::_READ12 READ12
struct _CDB::_WRITE12 WRITE12
LONGLONG QuadPart
Definition: typedefs.h:114
_In_ WDFREQUEST Request
Definition: wdfdevice.h:547
_Must_inspect_result_ _In_ PWDF_DPC_CONFIG _In_ PWDF_OBJECT_ATTRIBUTES _Out_ WDFDPC * Dpc
Definition: wdfdpc.h:112
#define WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(_attributes, _contexttype)
Definition: wdfobject.h:170
#define WdfObjectReference(Handle)
Definition: wdfobject.h:803
#define WdfObjectDereference(Handle)
Definition: wdfobject.h:826
@ WDF_REQUEST_SEND_OPTION_SYNCHRONOUS
Definition: wdfrequest.h:109
_In_ WDFIOTARGET Target
Definition: wdfrequest.h:306
_In_ WDFIOTARGET _In_ PWDF_REQUEST_COMPLETION_PARAMS Params
Definition: wdfrequest.h:308
@ WDF_REQUEST_REUSE_NO_FLAGS
Definition: wdfrequest.h:92
FORCEINLINE VOID WDF_REQUEST_REUSE_PARAMS_INIT(_Out_ PWDF_REQUEST_REUSE_PARAMS Params, _In_ ULONG Flags, _In_ NTSTATUS Status)
Definition: wdfrequest.h:364
_In_ ULONG _Out_opt_ PULONG RequiredLength
Definition: wmifuncs.h:30
__drv_aliasesMem FORCEINLINE PIO_STACK_LOCATION IoGetNextIrpStackLocation(_In_ PIRP Irp)
Definition: iofuncs.h:2695
#define IRP_MJ_SCSI
#define IRP_PAGING_IO
#define IRP_SYNCHRONOUS_PAGING_IO
_In_opt_ PVOID _In_opt_ PVOID SystemArgument1
Definition: ketypes.h:688
_In_opt_ PVOID DeferredContext
Definition: ketypes.h:687
_In_opt_ PVOID _In_opt_ PVOID _In_opt_ PVOID SystemArgument2
Definition: ketypes.h:689
#define BYTE_OFFSET(Va)
#define BYTES_TO_PAGES(Size)
#define MmPrepareMdlForReuse(_Mdl)
MDL
Definition: mmtypes.h:117
#define NT_ASSERT
Definition: rtlfuncs.h:3310
unsigned char UCHAR
Definition: xmlstorage.h:181