/*++

Copyright (C) Microsoft Corporation. All rights reserved.

Module Name:

    mmc.h

Abstract:

    Functions/macros for using the scratch buffer and scratch request/SRB
    in the serial I/O queue context

Author:

Environment:

    kernel mode only

Notes:


Revision History:

--*/

#ifndef __SCRATCH_H__
#define __SCRATCH_H__


_IRQL_requires_max_(APC_LEVEL)
VOID
ScratchBuffer_Deallocate(
    _Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension
    );

_IRQL_requires_max_(APC_LEVEL)
BOOLEAN
ScratchBuffer_Allocate(
    _Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension
    );

VOID
ScratchBuffer_ResetItems(
    _Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension,
    _In_ BOOLEAN                 ResetRequestHistory
    );

_IRQL_requires_max_(APC_LEVEL)
VOID
ScratchBuffer_SetupSrb(
    _Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension,
    _In_opt_ WDFREQUEST             OriginalRequest,
    _In_ ULONG                      MaximumTransferLength,
    _In_ BOOLEAN                    GetDataFromDevice
    );

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
    );

NTSTATUS
ScratchBuffer_SendSrb(
    _Inout_     PCDROM_DEVICE_EXTENSION DeviceExtension,
    _In_        BOOLEAN                 SynchronousSrb,
    _When_(SynchronousSrb, _Pre_null_)
    _When_(!SynchronousSrb, _In_opt_)
                PSRB_HISTORY_ITEM       *SrbHistoryItem
    );

NTSTATUS
ScratchBuffer_PerformNextReadWrite(
    _In_ PCDROM_DEVICE_EXTENSION  DeviceExtension,
    _In_ BOOLEAN                  FirstTry
    );

#if DBG
    #define ScratchBuffer_BeginUse(context) ScratchBuffer_BeginUseX((context), __FILE__, __LINE__)
#else
    #define ScratchBuffer_BeginUse(context) ScratchBuffer_BeginUseX((context), NULL, (ULONG)-1)
#endif

FORCEINLINE VOID ScratchBuffer_BeginUseX(_Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension, _In_opt_ LPCSTR File, ULONG Line)
{
    // NOTE: these are not "real" locks.  They are simply to help
    //       avoid multiple uses of the scratch buffer. Thus, it
    //       is not critical to have atomic operations here.
    PVOID tmp = InterlockedCompareExchangePointer((PVOID)&(DeviceExtension->ScratchContext.ScratchInUse), (PVOID)-1, NULL);
    NT_ASSERT(tmp == NULL);
    UNREFERENCED_PARAMETER(tmp); //defensive coding, avoid PREFAST warning.
    DeviceExtension->ScratchContext.ScratchInUseFileName = File;
    DeviceExtension->ScratchContext.ScratchInUseLineNumber = Line;
    ScratchBuffer_ResetItems(DeviceExtension, TRUE);
    RequestClearSendTime(DeviceExtension->ScratchContext.ScratchRequest);
    return;
}
FORCEINLINE VOID ScratchBuffer_EndUse(_Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension)
{
    // NOTE: these are not "real" locks.  They are simply to help
    //       avoid multiple uses of the scratch buffer.  Thus, it
    //       is not critical to have atomic operations here.

    // On lock release, we erase ScratchInUseFileName and ScratchInUseLineNumber _before_ releasing ScratchInUse,
    // because otherwise we may erase these after the lock has been acquired again by another thread. We store the
    // old values of ScratchInUseFileName and ScratchInUseLineNumber in local variables to facilitate debugging,
    // if the ASSERT at the end of the function is hit.
    PCSTR  scratchInUseFileName;
    ULONG  scratchInUseLineNumber;
    PVOID  tmp;

    scratchInUseFileName = DeviceExtension->ScratchContext.ScratchInUseFileName;
    scratchInUseLineNumber = DeviceExtension->ScratchContext.ScratchInUseLineNumber;
    UNREFERENCED_PARAMETER(scratchInUseFileName);
    UNREFERENCED_PARAMETER(scratchInUseLineNumber);
    DeviceExtension->ScratchContext.ScratchInUseFileName = NULL;
    DeviceExtension->ScratchContext.ScratchInUseLineNumber = 0;

    //
    // If we have used the PartialMdl in the scratch context we should notify MM that we will be reusing it
    // otherwise it may leak System VA if some one below us has mapped the same.
    //

    if (DeviceExtension->ScratchContext.PartialMdlIsBuilt != FALSE)
    {
        MmPrepareMdlForReuse(DeviceExtension->ScratchContext.PartialMdl);
        DeviceExtension->ScratchContext.PartialMdlIsBuilt = FALSE;
    }

    tmp = InterlockedCompareExchangePointer((PVOID)&(DeviceExtension->ScratchContext.ScratchInUse), NULL, (PVOID)-1);
    NT_ASSERT(tmp == ((PVOID)-1));
    UNREFERENCED_PARAMETER(tmp); //defensive coding, avoid PREFAST warning.
    return;
}

VOID
CompressSrbHistoryData(
    _Inout_  PSRB_HISTORY   RequestHistory
    );

VOID
ValidateSrbHistoryDataPresumptions(
    _In_     SRB_HISTORY const* RequestHistory
    );

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
ScratchBuffer_ExecuteCdbEx(
    _Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension,
    _In_opt_ WDFREQUEST             OriginalRequest,
    _In_ ULONG                      TransferSize,
    _In_ BOOLEAN                    GetDataFromDevice,
    _In_ PCDB                       Cdb,
    _In_ UCHAR                      OprationLength,
    _In_ ULONG                      TimeoutValue
    );

_IRQL_requires_max_(APC_LEVEL)
FORCEINLINE // __REACTOS__
NTSTATUS
ScratchBuffer_ExecuteCdb(
    _Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension,
    _In_opt_ WDFREQUEST             OriginalRequest,
    _In_ ULONG                      TransferSize,
    _In_ BOOLEAN                    GetDataFromDevice,
    _In_ PCDB                       Cdb,
    _In_ UCHAR                      OprationLength
    )
{
    return ScratchBuffer_ExecuteCdbEx(DeviceExtension,
                                      OriginalRequest,
                                      TransferSize,
                                      GetDataFromDevice,
                                      Cdb,
                                      OprationLength,
                                      0);
}

KDEFERRED_ROUTINE ScratchBuffer_ReadWriteTimerRoutine;

#endif //__SCRATCH_H__
