[][openwrt][mt7988][crypto][EIP197 DDK Porting]

[Description]
Add eip197 DDK(Driver Development Kit) and firmware
to eip197 package(crypto-eip)

eip197 DDK v5.6.1
eip197b-iew firmware v3.5

[Release-log]
N/A

Change-Id: I662327ecfbdac69742bf0b50362d7c28fc06372b
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/7895272
diff --git a/package-21.02/kernel/crypto-eip/src/ddk/slad/adapter_pec_dma.c b/package-21.02/kernel/crypto-eip/src/ddk/slad/adapter_pec_dma.c
new file mode 100644
index 0000000..4bf0a49
--- /dev/null
+++ b/package-21.02/kernel/crypto-eip/src/ddk/slad/adapter_pec_dma.c
@@ -0,0 +1,1989 @@
+/* adapter_pec_dma.c
+ *
+ * Packet Engine Control (PEC) API Implementation
+ * using DMA mode.
+ */
+
+/*****************************************************************************
+* Copyright (c) 2011-2022 by Rambus, Inc. and/or its subsidiaries.
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 2 of the License, or
+* any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+/*----------------------------------------------------------------------------
+ * This module implements (provides) the following interface(s):
+ */
+
+#include "api_pec.h"            // PEC_* (the API we implement here)
+
+
+/*----------------------------------------------------------------------------
+ * This module uses (requires) the following interface(s):
+ */
+
+// Default Adapter PEC configuration
+#include "c_adapter_pec.h"
+
+// DMABuf API
+#include "api_dmabuf.h"         // DMABuf_*
+
+// Adapter DMABuf internal API
+#include "adapter_dmabuf.h"
+
+// Adapter PEC device API
+#include "adapter_pecdev_dma.h" // Adapter_PECDev_*
+
+// Adapter Locking internal API
+#include "adapter_lock.h"       // Adapter_Lock_*
+
+#ifdef ADAPTER_PEC_ENABLE_SCATTERGATHER
+#include "api_pec_sg.h"         // PEC_SG_* (the API we implement here)
+#endif
+
+// Runtime Power Management Device Macros API
+#include "rpm_device_macros.h"  // RPM_*
+
+// Logging API
+#include "log.h"
+
+// Driver Framework DMAResource API
+#include "dmares_types.h"       // DMAResource_Handle_t
+#include "dmares_mgmt.h"        // DMAResource management functions
+#include "dmares_rw.h"          // DMAResource buffer access.
+#include "dmares_addr.h"        // DMAResource addr translation functions.
+#include "dmares_buf.h"         // DMAResource buffer allocations
+
+// Standard IOToken API
+#include "iotoken.h"
+
+// Driver Framework C Run-Time Library API
+#include "clib.h"               // memcpy, memset
+
+// Driver Framework Basic Definitions API
+#include "basic_defs.h"         // bool, uint32_t
+
+
+#ifndef ADAPTER_PE_MODE_DHM
+/*----------------------------------------------------------------------------
+ * Definitions and macros
+ */
+
+typedef struct
+{
+    void * User_p;
+    DMABuf_Handle_t SrcPkt_Handle;
+    DMABuf_Handle_t DstPkt_Handle;
+    DMABuf_Handle_t Token_Handle;
+    unsigned int Bypass_WordCount;
+} Adapter_SideChannelRecord_t;
+
+
+/* Side channel FIFO
+   - Normal operation: PEC_Packet_Put adds a record containing several
+                                      words for each packet.
+                       PEC_Packet_Get pops one record for each packet, fills
+                                      fields into result descriptor.
+   - Contiuous scatter mode: PEC_Scatter_Preload adds a record
+                                      with DestPkt_Handle only for each scatter
+                                      buffer.
+                             PEC_Packet_Get pops a record for each scatter
+                                      buffer used. Can do PostDMA for each
+                                      scatter buffer, will not fill in fields
+                                      in result descriptor.
+*/
+typedef struct
+{
+    int Size;
+    int ReadIndex;
+    int WriteIndex;
+    Adapter_SideChannelRecord_t Records[1 + ADAPTER_PEC_MAX_PACKETS +
+                                        ADAPTER_PEC_MAX_LOGICDESCR];
+} AdapterPEC_SideChannelFIFO_t;
+
+
+/*----------------------------------------------------------------------------
+ * Local variables
+ */
+static volatile bool PEC_IsInitialized[ADAPTER_PEC_DEVICE_COUNT];
+static volatile bool PEC_ContinuousScatter[ADAPTER_PEC_DEVICE_COUNT];
+
+// Lock and critical section for PEC_Init/Uninit()
+static ADAPTER_LOCK_DEFINE(AdapterPEC_InitLock);
+static Adapter_Lock_CS_t AdapterPEC_InitCS;
+
+// Locks and critical sections for PEC_Packet_Put()
+static Adapter_Lock_t AdapterPEC_PutLock[ADAPTER_PEC_DEVICE_COUNT];
+static Adapter_Lock_CS_t AdapterPEC_PutCS[ADAPTER_PEC_DEVICE_COUNT];
+
+// Locks and critical sections for PEC_Packet_Get()
+static Adapter_Lock_t AdapterPEC_GetLock[ADAPTER_PEC_DEVICE_COUNT];
+static Adapter_Lock_CS_t AdapterPEC_GetCS[ADAPTER_PEC_DEVICE_COUNT];
+
+static AdapterPEC_SideChannelFIFO_t
+Adapter_SideChannelFIFO[ADAPTER_PEC_DEVICE_COUNT];
+
+static struct
+{
+    volatile PEC_NotifyFunction_t ResultNotifyCB_p;
+    volatile unsigned int ResultsCount;
+
+    volatile PEC_NotifyFunction_t CommandNotifyCB_p;
+    volatile unsigned int CommandsCount;
+
+} PEC_Notify[ADAPTER_PEC_DEVICE_COUNT];
+
+
+#ifdef ADAPTER_PEC_INTERRUPTS_ENABLE
+/*----------------------------------------------------------------------------
+ * AdapterPEC_InterruptHandlerResultNotify
+ *
+ * This function is the interrupt handler for the PEC interrupt
+ * sources that indicate the arrival of a a result descriptor..There
+ * may be several interrupt sources.
+ *
+ * This function is used to invoke the PEC result notification callback.
+ */
+static void
+AdapterPEC_InterruptHandlerResultNotify(
+        const int nIRQ,
+        const unsigned int flags)
+{
+    unsigned int InterfaceId = Adapter_PECDev_IRQToInferfaceId(nIRQ);
+
+    IDENTIFIER_NOT_USED(flags);
+
+    if (InterfaceId >= ADAPTER_PEC_DEVICE_COUNT)
+    {
+        LOG_CRIT("AdapterPEC_InterruptHandlerResultNotify"
+                 "InterfaceId out of range\n");
+        return;
+    }
+
+    Adapter_PECDev_Disable_ResultIRQ(InterfaceId);
+
+    LOG_INFO("AdapterPEC_InterruptHandlerResultNotify: Enter\n");
+
+    if (PEC_Notify[InterfaceId].ResultNotifyCB_p != NULL)
+    {
+        PEC_NotifyFunction_t CBFunc_p;
+
+        // Keep the callback on stack to allow registration
+        // of another result notify request from callback
+        CBFunc_p = PEC_Notify[InterfaceId].ResultNotifyCB_p;
+
+        PEC_Notify[InterfaceId].ResultNotifyCB_p = NULL;
+        PEC_Notify[InterfaceId].ResultsCount = 0;
+
+        LOG_INFO(
+            "AdapterPEC_InterruptHandlerResultNotify: "
+            "Invoking PEC result notify callback for interface %d\n",
+            InterfaceId);
+
+        CBFunc_p();
+    }
+}
+
+
+/*----------------------------------------------------------------------------
+ * AdapterPEC_InterruptHandlerCommandNotify
+ *
+ * This function is the interrupt handler for the PEC interrupt sources.that
+ * indicate that there is again freee space for new command descriptors.
+ *
+ * This function is used to invoke the PEC command notification callback.
+ */
+static void
+AdapterPEC_InterruptHandlerCommandNotify(
+        const int nIRQ,
+        const unsigned int flags)
+{
+    unsigned int InterfaceId = Adapter_PECDev_IRQToInferfaceId(nIRQ);
+
+    IDENTIFIER_NOT_USED(flags);
+
+    if (InterfaceId >= ADAPTER_PEC_DEVICE_COUNT)
+    {
+        LOG_CRIT("AdapterPEC_InterruptHandlerCommandNotify"
+                 "InterfaceId out of range\n");
+        return;
+    }
+
+    Adapter_PECDev_Disable_CommandIRQ(InterfaceId);
+
+    LOG_INFO("AdapterPEC_InterruptHandlerCommandNotify: Enter\n");
+
+    if (PEC_Notify[InterfaceId].CommandNotifyCB_p != NULL)
+    {
+        PEC_NotifyFunction_t CBFunc_p;
+
+        // Keep the callback on stack to allow registration
+        // of another command notify request from callback
+        CBFunc_p = PEC_Notify[InterfaceId].CommandNotifyCB_p;
+
+        PEC_Notify[InterfaceId].CommandNotifyCB_p = NULL;
+        PEC_Notify[InterfaceId].CommandsCount = 0;
+
+        LOG_INFO(
+            "AdapterPEC_InterruptHandlerCommandNotify: "
+            "Invoking PEC command notify callback interface=%d\n",
+            InterfaceId);
+
+        CBFunc_p();
+    }
+}
+#endif /* ADAPTER_PEC_INTERRUPTS_ENABLE */
+
+
+/*----------------------------------------------------------------------------
+ * Adapter_MakeCommandNotify_CallBack
+ */
+static inline void
+Adapter_MakeCommandNotify_CallBack(unsigned int InterfaceId)
+{
+    unsigned int PacketSlotsEmptyCount;
+
+    if (InterfaceId >= ADAPTER_PEC_DEVICE_COUNT)
+        return;
+
+    if (PEC_Notify[InterfaceId].CommandNotifyCB_p != NULL)
+    {
+        PacketSlotsEmptyCount = Adapter_PECDev_GetFreeSpace(InterfaceId);
+
+        if (PEC_Notify[InterfaceId].CommandsCount <= PacketSlotsEmptyCount)
+        {
+            PEC_NotifyFunction_t CBFunc_p;
+
+            // Keep the callback on stack to allow registeration
+            // of another result notify request from callback
+            CBFunc_p = PEC_Notify[InterfaceId].CommandNotifyCB_p;
+
+            PEC_Notify[InterfaceId].CommandNotifyCB_p = NULL;
+            PEC_Notify[InterfaceId].CommandsCount = 0;
+
+            LOG_INFO(
+                "PEC_Packet_Get: "
+                "Invoking command notify callback\n");
+
+            CBFunc_p();
+        }
+    }
+}
+
+
+/*----------------------------------------------------------------------------
+ * Adapter_PECResgisterSA_BounceIfRequired
+ *
+ * Returns false in case of error.
+ * Allocate a bounce buffer and copy the data in case this if required.
+ */
+#ifndef ADAPTER_PEC_REMOVE_BOUNCEBUFFERS
+static bool
+Adapter_PECRegisterSA_BounceIfRequired(
+        DMAResource_Handle_t *DMAHandle_p)
+{
+    DMAResource_Handle_t DMAHandle = *DMAHandle_p;
+    DMAResource_Record_t * Rec_p;
+    DMAResource_AddrPair_t BounceHostAddr;
+    void * HostAddr;
+    int dmares;
+
+    // skip null handles
+    if (!DMAResource_IsValidHandle(DMAHandle))
+        return true;    // no error
+
+    Rec_p = DMAResource_Handle2RecordPtr(DMAHandle);
+
+
+    // skip proper buffers
+    if (!Adapter_DMAResource_IsForeignAllocated(DMAHandle))
+    {
+        Rec_p->bounce.Bounce_Handle = NULL;
+        return true;    // no error
+    }
+
+    {
+        DMAResource_Properties_t BounceProperties;
+
+        // used as uint32_t array
+        BounceProperties.Alignment  = Adapter_DMAResource_Alignment_Get();
+        BounceProperties.Bank       = ADAPTER_PEC_BANK_SA;
+        BounceProperties.fCached    = false;
+        BounceProperties.Size       = Rec_p->Props.Size;
+
+        HostAddr = Adapter_DMAResource_HostAddr(DMAHandle);
+
+        dmares = DMAResource_Alloc(
+                     BounceProperties,
+                     &BounceHostAddr,
+                     &Rec_p->bounce.Bounce_Handle);
+
+        // bounce buffer handle is stored in the DMA Resource Record
+        // of the original buffer, which links the two
+        // this will be used when freeing the buffer
+        // but also when the SA is referenced in packet put
+
+        if (dmares != 0)
+        {
+            LOG_CRIT(
+                "PEC_SA_Register: "
+                "Failed to alloc bounce buffer (error %d)\n",
+                dmares);
+            return false;   // error!
+        }
+        LOG_INFO(
+            "PEC_SA_Register: "
+            "Bouncing SA: %p to %p\n",
+            DMAHandle,
+            Rec_p->bounce.Bounce_Handle);
+#ifdef ADAPTER_PEC_ARMRING_ENABLE_SWAP
+        DMAResource_SwapEndianness_Set(Rec_p->bounce.Bounce_Handle, true);
+#endif
+
+    }
+
+    // copy the data to the bounce buffer
+    memcpy(
+        BounceHostAddr.Address_p,
+        HostAddr,
+        Rec_p->Props.Size);
+
+    *DMAHandle_p = Rec_p->bounce.Bounce_Handle;
+    return true;        // no error
+}
+#endif /* ADAPTER_PEC_REMOVE_BOUNCEBUFFERS */
+
+
+/*----------------------------------------------------------------------------
+ * Adapter_FIFO_Put
+ *
+ * Put packet information into the side channel FIFO
+ */
+static bool
+Adapter_FIFO_Put(AdapterPEC_SideChannelFIFO_t *FIFO,
+                 void *User_p,
+                 DMABuf_Handle_t SrcPkt_Handle,
+                 DMABuf_Handle_t DstPkt_Handle,
+                 DMABuf_Handle_t Token_Handle,
+                 unsigned int Bypass_WordCount)
+{
+    int WriteIndex = FIFO->WriteIndex;
+    int ReadIndex = FIFO->ReadIndex;
+    if (WriteIndex == ReadIndex - 1 ||
+        (ReadIndex == 0 && WriteIndex == FIFO->Size - 1))
+    {
+        LOG_CRIT("Side channel FIFO full\n");
+        return false;
+    }
+    FIFO->Records[WriteIndex].User_p = User_p;
+    FIFO->Records[WriteIndex].SrcPkt_Handle = SrcPkt_Handle;
+    FIFO->Records[WriteIndex].DstPkt_Handle = DstPkt_Handle;
+
+    FIFO->Records[WriteIndex].Token_Handle = Token_Handle;
+    if (!DMABuf_Handle_IsSame(&Token_Handle, &DMABuf_NULLHandle))
+    {
+        FIFO->Records[WriteIndex].Bypass_WordCount = Bypass_WordCount;
+    }
+
+    WriteIndex += 1;
+    if (WriteIndex == FIFO->Size)
+        WriteIndex = 0;
+    FIFO->WriteIndex = WriteIndex;
+    return true;
+}
+
+
+/*----------------------------------------------------------------------------
+ * Adapter_FIFO_Get
+ *
+ * Get and remove the oldest entry from the side channel FIFO.
+ */
+static bool
+Adapter_FIFO_Get(AdapterPEC_SideChannelFIFO_t *FIFO,
+                 void **User_p,
+                 DMABuf_Handle_t *SrcPkt_Handle_p,
+                 DMABuf_Handle_t *DstPkt_Handle_p,
+                 DMABuf_Handle_t *Token_Handle_p,
+                 unsigned int *Bypass_WordCount_p)
+{
+    int WriteIndex = FIFO->WriteIndex;
+    int ReadIndex = FIFO->ReadIndex;
+    if (WriteIndex == ReadIndex)
+    {
+        LOG_CRIT("Trying to read from empty FIFO\n");
+        return false;
+    }
+    if (User_p)
+        *User_p = FIFO->Records[ReadIndex].User_p;
+    if (SrcPkt_Handle_p)
+        *SrcPkt_Handle_p = FIFO->Records[ReadIndex].SrcPkt_Handle;
+    *DstPkt_Handle_p = FIFO->Records[ReadIndex].DstPkt_Handle;
+
+    if (Token_Handle_p)
+        *Token_Handle_p = FIFO->Records[ReadIndex].Token_Handle;
+    if (Token_Handle_p != NULL &&
+        !DMABuf_Handle_IsSame(Token_Handle_p, &DMABuf_NULLHandle) &&
+        Bypass_WordCount_p != NULL)
+        *Bypass_WordCount_p = FIFO->Records[ReadIndex].Bypass_WordCount;
+
+    ReadIndex += 1;
+    if (ReadIndex == FIFO->Size)
+        ReadIndex = 0;
+    FIFO->ReadIndex = ReadIndex;
+    return true;
+}
+
+
+/*----------------------------------------------------------------------------
+ * Adapter_FIFO_Withdraw
+ *
+ * Withdraw the most recently added record from the side channel FIFO.
+ */
+static void
+Adapter_FIFO_Withdraw(
+        AdapterPEC_SideChannelFIFO_t *FIFO)
+{
+    int WriteIndex = FIFO->WriteIndex;
+    if (WriteIndex == FIFO->ReadIndex)
+    {
+        LOG_CRIT("Adapter_FIFO_Withdraw: FIFO is empty\n");
+    }
+    if (WriteIndex == 0)
+        WriteIndex = FIFO->Size - 1;
+    else
+        WriteIndex -= 1;
+    FIFO->WriteIndex = WriteIndex;
+}
+
+
+/* Adapter_Packet_Prepare
+ *
+ * In case of bounce buffers, allocate bounce buffers for the packet and
+ * the packet token.
+ * Copy source packet and token into the bounce buffers.
+ * Perform PreDMA on all packet buffers (source, destination and token).
+ */
+static PEC_Status_t
+Adapter_Packet_Prepare(
+        const unsigned int InterfaceId,
+        const PEC_CommandDescriptor_t *Cmd_p)
+{
+    DMAResource_Handle_t SrcPkt_Handle, DstPkt_Handle, Token_Handle;
+#ifdef ADAPTER_PEC_ENABLE_SCATTERGATHER
+    unsigned int ParticleCount;
+    unsigned int i;
+    DMABuf_Handle_t ParticleHandle;
+    DMAResource_Handle_t DMARes_Handle;
+    uint8_t * DummyPtr;
+    unsigned int ParticleSize;
+#endif
+
+    SrcPkt_Handle =
+        Adapter_DMABuf_Handle2DMAResourceHandle(Cmd_p->SrcPkt_Handle);
+    DstPkt_Handle =
+        Adapter_DMABuf_Handle2DMAResourceHandle(Cmd_p->DstPkt_Handle);
+    Token_Handle = Adapter_DMABuf_Handle2DMAResourceHandle(Cmd_p->Token_Handle);
+
+    if (!DMAResource_IsValidHandle(SrcPkt_Handle) &&
+        !DMAResource_IsValidHandle(DstPkt_Handle))
+        return PEC_STATUS_OK; // For record invalidation in the Record Cache
+    else if (!DMAResource_IsValidHandle(SrcPkt_Handle) ||
+             (!DMAResource_IsValidHandle(DstPkt_Handle) &&
+              !PEC_ContinuousScatter[InterfaceId]))
+    {
+        LOG_CRIT("PEC_Packet_Put: invalid source or destination handle\n");
+        return PEC_ERROR_BAD_PARAMETER;
+    }
+
+    // Token handle
+    if (DMAResource_IsValidHandle(Token_Handle))
+    {
+#ifndef ADAPTER_PEC_REMOVE_BOUNCEBUFFERS
+        DMAResource_Record_t * Rec_p =
+            DMAResource_Handle2RecordPtr(Token_Handle);
+        if (Adapter_DMAResource_IsForeignAllocated(Token_Handle))
+        {
+            // Bounce buffer required.
+            DMAResource_AddrPair_t BounceHostAddr;
+            void * HostAddr;
+            int dmares;
+            DMAResource_Properties_t BounceProperties;
+
+            // used as uint32_t array
+            BounceProperties.Alignment  = Adapter_DMAResource_Alignment_Get();
+            BounceProperties.Bank       = ADAPTER_PEC_BANK_TOKEN;
+            BounceProperties.fCached    = false;
+            BounceProperties.Size       = Rec_p->Props.Size;
+
+            HostAddr = Adapter_DMAResource_HostAddr(Token_Handle);
+
+            dmares = DMAResource_Alloc(
+                BounceProperties,
+                &BounceHostAddr,
+                &Rec_p->bounce.Bounce_Handle);
+
+            // bounce buffer handle is stored in the DMA Resource Record
+            // of the original buffer, which links the two
+            // this will be used when freeing the buffer
+            // but also when obtaining the bus address.
+
+            if (dmares != 0)
+            {
+                LOG_CRIT(
+                    "PEC_Packet_Put: "
+                    "Failed to alloc bounce buffer (error %d)\n",
+                dmares);
+                return PEC_ERROR_INTERNAL;   // error!
+            }
+
+            LOG_INFO(
+                "PEC_Packet_Putr: "
+                "Bouncing Token: %p to %p\n",
+                Token_Handle,
+                Rec_p->bounce.Bounce_Handle);
+
+            // copy the data to the bounce buffer
+            memcpy(
+                BounceHostAddr.Address_p,
+                HostAddr,
+                Rec_p->Props.Size);
+
+            Token_Handle = Rec_p->bounce.Bounce_Handle;
+        }
+        else
+        {
+            Rec_p->bounce.Bounce_Handle = NULL;
+        }
+#endif // !ADAPTER_PEC_REMOVE_BOUNCEBUFFERS
+
+#ifdef ADAPTER_PEC_ARMRING_ENABLE_SWAP
+        // Convert token data to packet engine endianness format
+        DMAResource_SwapEndianness_Set(Token_Handle, true);
+
+        DMAResource_Write32Array(
+            Token_Handle,
+            0,
+            Cmd_p->Token_WordCount,
+            Adapter_DMAResource_HostAddr(Token_Handle));
+#endif // ADAPTER_PEC_ARMRING_ENABLE_SWAP
+
+        DMAResource_PreDMA(Token_Handle, 0, 0);
+    }
+
+    // Source packet handle
+#ifdef ADAPTER_PEC_ENABLE_SCATTERGATHER
+    PEC_SGList_GetCapacity(Cmd_p->SrcPkt_Handle, &ParticleCount);
+
+    if (ParticleCount > 0)
+    {
+        for (i=0; i<ParticleCount; i++)
+        {
+            PEC_SGList_Read(Cmd_p->SrcPkt_Handle,
+                            i,
+                            &ParticleHandle,
+                            &ParticleSize,
+                            &DummyPtr);
+            DMARes_Handle =
+                Adapter_DMABuf_Handle2DMAResourceHandle(ParticleHandle);
+            DMAResource_PreDMA(DMARes_Handle, 0, 0);
+        }
+    }
+    else
+#endif
+    { // Not a gather packet,
+#ifndef ADAPTER_PEC_REMOVE_BOUNCEBUFFERS
+        DMAResource_Record_t * Rec_p =
+            DMAResource_Handle2RecordPtr(SrcPkt_Handle);
+        DMAResource_Record_t * Dst_Rec_p =
+            DMAResource_Handle2RecordPtr(DstPkt_Handle);
+        if (Adapter_DMAResource_IsForeignAllocated(SrcPkt_Handle) ||
+            Adapter_DMAResource_IsForeignAllocated(DstPkt_Handle))
+        {
+            // Bounce buffer required. Use a single bounce buffer for
+            // both the source and the destination packet.
+            DMAResource_AddrPair_t BounceHostAddr;
+            void * HostAddr;
+            int dmares;
+            DMAResource_Properties_t BounceProperties;
+
+            // used as uint32_t array
+            BounceProperties.Alignment  = Adapter_DMAResource_Alignment_Get();
+            BounceProperties.Bank       = ADAPTER_PEC_BANK_PACKET;
+            BounceProperties.fCached    = false;
+            BounceProperties.Size       = MAX(Rec_p->Props.Size,
+                                              Dst_Rec_p->Props.Size);
+
+            HostAddr = Adapter_DMAResource_HostAddr(SrcPkt_Handle);
+
+            dmares = DMAResource_Alloc(
+                BounceProperties,
+                &BounceHostAddr,
+                &Rec_p->bounce.Bounce_Handle);
+
+            // bounce buffer handle is stored in the DMA Resource Record
+            // of the original buffer, which links the two
+            // this will be used when freeing the buffer
+            // but also when obtaining the bus address.
+
+            if (dmares != 0)
+            {
+                LOG_CRIT(
+                    "PEC_Packet_Put: "
+                    "Failed to alloc bounce buffer (error %d)\n",
+                dmares);
+                return PEC_ERROR_INTERNAL;   // error!
+            }
+            LOG_INFO(
+                "PEC_Packet_Putr: "
+                "Bouncing Packet: %p to %p\n",
+                SrcPkt_Handle,
+                Rec_p->bounce.Bounce_Handle);
+
+
+            // copy the data to the bounce buffer
+            memcpy(
+                BounceHostAddr.Address_p,
+                HostAddr,
+                Rec_p->Props.Size);
+
+            DstPkt_Handle = SrcPkt_Handle = Rec_p->bounce.Bounce_Handle;
+
+            Dst_Rec_p->bounce.Bounce_Handle = Rec_p->bounce.Bounce_Handle;
+        }
+        else
+        {
+            Rec_p->bounce.Bounce_Handle = NULL;
+            Dst_Rec_p->bounce.Bounce_Handle = NULL;
+        }
+#endif
+        DMAResource_PreDMA(SrcPkt_Handle, 0, 0);
+    }
+    // Destination packet handle, not for continuous scatter.
+    if (PEC_ContinuousScatter[InterfaceId])
+        return PEC_STATUS_OK;
+#ifdef ADAPTER_PEC_ENABLE_SCATTERGATHER
+    PEC_SGList_GetCapacity(Cmd_p->DstPkt_Handle, &ParticleCount);
+
+    if (ParticleCount > 0)
+    {
+        for (i=0; i<ParticleCount; i++)
+        {
+            PEC_SGList_Read(Cmd_p->DstPkt_Handle,
+                            i,
+                            &ParticleHandle,
+                            &ParticleSize,
+                            &DummyPtr);
+            DMARes_Handle =
+                Adapter_DMABuf_Handle2DMAResourceHandle(ParticleHandle);
+            DMAResource_PreDMA(DMARes_Handle, 0, 0);
+        }
+    }
+    else
+#endif
+    if (SrcPkt_Handle != DstPkt_Handle)
+    {
+        // Only if source and destination are distinct.
+        // When bounce buffers were used, these are not distinct.
+        DMAResource_PreDMA(DstPkt_Handle, 0, 0);
+    }
+    return PEC_STATUS_OK;
+}
+
+
+/* Adapter_Packet_Finalize
+ *
+ * Perform PostDMA on all DMA buffers (source, destination and token).
+ * Copy the destination packet from the bounce buffer into the final location.
+ * Deallocate any bounce buffers (packet and token).
+ */
+static PEC_Status_t
+Adapter_Packet_Finalize(
+        DMABuf_Handle_t DMABuf_SrcPkt_Handle,
+        DMABuf_Handle_t DMABuf_DstPkt_Handle,
+        DMABuf_Handle_t DMABuf_Token_Handle)
+{
+    DMAResource_Handle_t SrcPkt_Handle, DstPkt_Handle, Token_Handle;
+
+#ifdef ADAPTER_PEC_ENABLE_SCATTERGATHER
+    unsigned int ParticleCount;
+    unsigned int i;
+    DMABuf_Handle_t ParticleHandle;
+    DMAResource_Handle_t DMARes_Handle;
+    uint8_t * DummyPtr;
+    unsigned int ParticleSize;
+#endif
+
+    SrcPkt_Handle =
+        Adapter_DMABuf_Handle2DMAResourceHandle(DMABuf_SrcPkt_Handle);
+    DstPkt_Handle =
+        Adapter_DMABuf_Handle2DMAResourceHandle(DMABuf_DstPkt_Handle);
+
+    if (!DMAResource_IsValidHandle(SrcPkt_Handle) &&
+        !DMAResource_IsValidHandle(DstPkt_Handle))
+        return PEC_STATUS_OK; // For record invalidation in the Record Cache
+
+    Token_Handle = Adapter_DMABuf_Handle2DMAResourceHandle(DMABuf_Token_Handle);
+
+    // Token Handle.
+    if (DMAResource_IsValidHandle(Token_Handle))
+    {
+#ifndef ADAPTER_PEC_REMOVE_BOUNCEBUFFERS
+        DMAResource_Record_t * Rec_p =
+            DMAResource_Handle2RecordPtr(Token_Handle);
+        if (Rec_p->bounce.Bounce_Handle != NULL)
+        {
+            // Post DMA and release the bounce buffer.
+            DMAResource_PostDMA(Rec_p->bounce.Bounce_Handle, 0, 0);
+            DMAResource_Release(Rec_p->bounce.Bounce_Handle);
+        }
+        else
+#endif
+        {
+            DMAResource_PostDMA(Token_Handle, 0, 0);
+        }
+    }
+    // Destination packet handle
+
+#ifdef ADAPTER_PEC_ENABLE_SCATTERGATHER
+    PEC_SGList_GetCapacity(DMABuf_DstPkt_Handle, &ParticleCount);
+
+    if (ParticleCount > 0)
+    {
+        for (i=0; i<ParticleCount; i++)
+        {
+            PEC_SGList_Read(DMABuf_DstPkt_Handle,
+                            i,
+                            &ParticleHandle,
+                            &ParticleSize,
+                            &DummyPtr);
+            DMARes_Handle =
+                Adapter_DMABuf_Handle2DMAResourceHandle(ParticleHandle);
+            DMAResource_PostDMA(DMARes_Handle, 0, 0);
+        }
+    }
+    else
+#endif
+    {
+#ifndef ADAPTER_PEC_REMOVE_BOUNCEBUFFERS
+        DMAResource_Record_t * Rec_p =
+            DMAResource_Handle2RecordPtr(DstPkt_Handle);
+        void * HostAddr = Adapter_DMAResource_HostAddr(DstPkt_Handle);
+        if (Rec_p->bounce.Bounce_Handle != NULL)
+        {
+            void * BounceHostAddr =
+                Adapter_DMAResource_HostAddr(Rec_p->bounce.Bounce_Handle);
+            // Post DMA, copy and release the bounce buffer.
+            DMAResource_PostDMA(Rec_p->bounce.Bounce_Handle, 0, 0);
+
+            memcpy( HostAddr, BounceHostAddr, Rec_p->Props.Size);
+
+            DMAResource_Release(Rec_p->bounce.Bounce_Handle);
+            SrcPkt_Handle = DstPkt_Handle;
+        }
+        else
+#endif
+        {
+            DMAResource_PostDMA(DstPkt_Handle, 0, 0);
+        }
+
+    }
+    // Source packet handle
+#ifdef ADAPTER_PEC_ENABLE_SCATTERGATHER
+    PEC_SGList_GetCapacity(DMABuf_SrcPkt_Handle, &ParticleCount);
+
+    if (ParticleCount > 0)
+    {
+        for (i=0; i<ParticleCount; i++)
+        {
+            PEC_SGList_Read(DMABuf_SrcPkt_Handle,
+                            i,
+                            &ParticleHandle,
+                            &ParticleSize,
+                            &DummyPtr);
+            DMARes_Handle =
+                Adapter_DMABuf_Handle2DMAResourceHandle(ParticleHandle);
+            DMAResource_PostDMA(DMARes_Handle, 0, 0);
+        }
+    }
+    else
+#endif
+    if (SrcPkt_Handle != DstPkt_Handle)
+    {
+        // Only if source and destination are distinct.
+        // When bounce buffers were used, these are not distinct.
+        DMAResource_PostDMA(SrcPkt_Handle, 0, 0);
+    }
+
+    return PEC_STATUS_OK;
+}
+
+
+#ifdef ADAPTER_PEC_RPM_EIP202_DEVICE0_ID
+/*----------------------------------------------------------------------------
+ * AdapterPEC_Resume
+ */
+static int
+AdapterPEC_Resume(void * p)
+{
+    int InterfaceId = *(int *)p;
+
+    if (InterfaceId < 0 || InterfaceId < ADAPTER_PEC_RPM_EIP202_DEVICE0_ID)
+        return -3; // error
+
+    InterfaceId -= ADAPTER_PEC_RPM_EIP202_DEVICE0_ID;
+
+    return Adapter_PECDev_Resume(InterfaceId);
+}
+
+
+/*----------------------------------------------------------------------------
+ * AdapterPEC_Suspend
+ */
+static int
+AdapterPEC_Suspend(void * p)
+{
+    int InterfaceId = *(int *)p;
+
+    if (InterfaceId < 0 || InterfaceId < ADAPTER_PEC_RPM_EIP202_DEVICE0_ID)
+        return -3; // error
+
+    InterfaceId -= ADAPTER_PEC_RPM_EIP202_DEVICE0_ID;
+
+    return Adapter_PECDev_Suspend(InterfaceId);
+}
+#endif
+
+
+/*----------------------------------------------------------------------------
+ * PEC_Capabilities_Get
+ */
+PEC_Status_t
+PEC_Capabilities_Get(
+        PEC_Capabilities_t * const Capabilities_p)
+{
+    return Adapter_PECDev_Capabilities_Get(Capabilities_p);
+}
+
+
+/*----------------------------------------------------------------------------
+ * PEC_Init
+ */
+PEC_Status_t
+PEC_Init(
+        const unsigned int InterfaceId,
+        const PEC_InitBlock_t * const InitBlock_p)
+{
+    LOG_INFO("\n\t PEC_Init \n");
+
+    if (!InitBlock_p)
+        return PEC_ERROR_BAD_PARAMETER;
+
+    if (InterfaceId >= ADAPTER_PEC_DEVICE_COUNT)
+        return PEC_ERROR_BAD_PARAMETER;
+
+    Adapter_Lock_CS_Set(&AdapterPEC_InitCS, &AdapterPEC_InitLock);
+
+    if (!Adapter_Lock_CS_Enter(&AdapterPEC_InitCS))
+        return PEC_STATUS_BUSY;
+
+    // ensure we init only once
+    if (PEC_IsInitialized[InterfaceId])
+    {
+        Adapter_Lock_CS_Leave(&AdapterPEC_InitCS);
+        return PEC_STATUS_OK;
+    }
+    PEC_ContinuousScatter[InterfaceId] = InitBlock_p->fContinuousScatter;
+
+    // Allocate the Put lock
+    AdapterPEC_PutLock[InterfaceId] = Adapter_Lock_Alloc();
+    if (AdapterPEC_PutLock[InterfaceId] == NULL)
+    {
+        LOG_CRIT("PEC_Init: PutLock allocation failed\n");
+        Adapter_Lock_CS_Leave(&AdapterPEC_InitCS);
+        return PEC_ERROR_INTERNAL;
+    }
+    Adapter_Lock_CS_Set(&AdapterPEC_PutCS[InterfaceId],
+                         AdapterPEC_PutLock[InterfaceId]);
+
+    // Allocate the Get lock
+    AdapterPEC_GetLock[InterfaceId] = Adapter_Lock_Alloc();
+    if (AdapterPEC_GetLock[InterfaceId] == NULL)
+    {
+        LOG_CRIT("PEC_Init: GetLock allocation failed\n");
+        Adapter_Lock_Free(AdapterPEC_PutLock[InterfaceId]);
+        Adapter_Lock_CS_Leave(&AdapterPEC_InitCS);
+        return PEC_ERROR_INTERNAL;
+    }
+    Adapter_Lock_CS_Set(&AdapterPEC_GetCS[InterfaceId],
+                         AdapterPEC_GetLock[InterfaceId]);
+
+    ZEROINIT(PEC_Notify[InterfaceId]);
+
+    if (RPM_DEVICE_INIT_START_MACRO(ADAPTER_PEC_RPM_EIP202_DEVICE0_ID + InterfaceId,
+                                    AdapterPEC_Suspend,
+                                    AdapterPEC_Resume) != RPM_SUCCESS)
+        return PEC_ERROR_INTERNAL;
+
+    // Init the device
+    if (Adapter_PECDev_Init(InterfaceId, InitBlock_p) != PEC_STATUS_OK)
+    {
+        (void)RPM_DEVICE_INIT_STOP_MACRO(ADAPTER_PEC_RPM_EIP202_DEVICE0_ID + InterfaceId);
+        LOG_CRIT("PEC_Init: Adapter_PECDev_Init failed\n");
+        Adapter_Lock_Free(AdapterPEC_PutLock[InterfaceId]);
+        Adapter_Lock_Free(AdapterPEC_GetLock[InterfaceId]);
+        Adapter_Lock_CS_Leave(&AdapterPEC_InitCS);
+        return PEC_ERROR_INTERNAL;
+    }
+
+    (void)RPM_DEVICE_INIT_STOP_MACRO(ADAPTER_PEC_RPM_EIP202_DEVICE0_ID + InterfaceId);
+
+    Adapter_SideChannelFIFO[InterfaceId].Size =
+        sizeof(Adapter_SideChannelFIFO[InterfaceId].Records) /
+        sizeof(Adapter_SideChannelFIFO[InterfaceId].Records[0]);
+    Adapter_SideChannelFIFO[InterfaceId].WriteIndex = 0;
+    Adapter_SideChannelFIFO[InterfaceId].ReadIndex = 0;
+
+#ifdef ADAPTER_PEC_INTERRUPTS_ENABLE
+    // enable the descriptor done interrupt
+    LOG_INFO("PEC_Init: Registering interrupt handler\n");
+
+    Adapter_PECDev_SetResultHandler(
+            InterfaceId,
+            AdapterPEC_InterruptHandlerResultNotify);
+
+    Adapter_PECDev_SetCommandHandler(
+            InterfaceId,
+            AdapterPEC_InterruptHandlerCommandNotify);
+#endif /* ADAPTER_PEC_INTERRUPTS_ENABLE */
+
+    PEC_IsInitialized[InterfaceId] = true;
+
+    LOG_INFO("\n\t PEC_Init done \n");
+
+    Adapter_Lock_CS_Leave(&AdapterPEC_InitCS);
+
+    return PEC_STATUS_OK;
+}
+
+
+/*----------------------------------------------------------------------------
+ * PEC_UnInit
+ */
+PEC_Status_t
+PEC_UnInit(
+        const unsigned int InterfaceId)
+{
+    LOG_INFO("\n\t PEC_UnInit \n");
+
+    if (InterfaceId >= ADAPTER_PEC_DEVICE_COUNT)
+        return PEC_ERROR_BAD_PARAMETER;
+
+    Adapter_Lock_CS_Set(&AdapterPEC_InitCS, &AdapterPEC_InitLock);
+
+    if (!Adapter_Lock_CS_Enter(&AdapterPEC_InitCS))
+        return PEC_STATUS_BUSY;
+
+    // ensure we uninit only once
+    if (!PEC_IsInitialized[InterfaceId])
+    {
+        Adapter_Lock_CS_Leave(&AdapterPEC_InitCS);
+        return PEC_STATUS_OK;
+    }
+
+    if (!Adapter_Lock_CS_Enter(&AdapterPEC_PutCS[InterfaceId]))
+    {
+        Adapter_Lock_CS_Leave(&AdapterPEC_InitCS);
+        return PEC_STATUS_BUSY;
+    }
+
+    if (!Adapter_Lock_CS_Enter(&AdapterPEC_GetCS[InterfaceId]))
+    {
+        Adapter_Lock_CS_Leave(&AdapterPEC_PutCS[InterfaceId]);
+        Adapter_Lock_CS_Leave(&AdapterPEC_InitCS);
+        return PEC_STATUS_BUSY;
+    }
+
+    if (RPM_DEVICE_UNINIT_START_MACRO(ADAPTER_PEC_RPM_EIP202_DEVICE0_ID + InterfaceId,
+                                true) != RPM_SUCCESS)
+    {
+        Adapter_Lock_CS_Leave(&AdapterPEC_PutCS[InterfaceId]);
+        Adapter_Lock_CS_Leave(&AdapterPEC_InitCS);
+        return PEC_ERROR_INTERNAL;
+    }
+
+#ifdef ADAPTER_PEC_INTERRUPTS_ENABLE
+    Adapter_PECDev_Disable_ResultIRQ(InterfaceId);
+    Adapter_PECDev_Disable_CommandIRQ(InterfaceId);
+#endif
+
+    Adapter_PECDev_UnInit(InterfaceId);
+
+    PEC_IsInitialized[InterfaceId] = false;
+
+    (void)RPM_DEVICE_UNINIT_STOP_MACRO(ADAPTER_PEC_RPM_EIP202_DEVICE0_ID + InterfaceId);
+
+    Adapter_Lock_CS_Leave(&AdapterPEC_GetCS[InterfaceId]);
+    Adapter_Lock_CS_Leave(&AdapterPEC_PutCS[InterfaceId]);
+
+    // Free Get lock
+    Adapter_Lock_Free(Adapter_Lock_CS_Get(&AdapterPEC_GetCS[InterfaceId]));
+    Adapter_Lock_CS_Set(&AdapterPEC_GetCS[InterfaceId], Adapter_Lock_NULL);
+
+    // Free Put lock
+    Adapter_Lock_Free(Adapter_Lock_CS_Get(&AdapterPEC_PutCS[InterfaceId]));
+    Adapter_Lock_CS_Set(&AdapterPEC_PutCS[InterfaceId], Adapter_Lock_NULL);
+
+    LOG_INFO("\n\t PEC_UnInit done \n");
+
+    Adapter_Lock_CS_Leave(&AdapterPEC_InitCS);
+
+    return PEC_STATUS_OK;
+}
+
+
+/*----------------------------------------------------------------------------
+ * PEC_SA_Register
+ */
+PEC_Status_t
+PEC_SA_Register(
+        const unsigned int InterfaceId,
+        DMABuf_Handle_t SA_Handle1,
+        DMABuf_Handle_t SA_Handle2,
+        DMABuf_Handle_t SA_Handle3)
+{
+    DMAResource_Handle_t DMAHandle1, DMAHandle2, DMAHandle3;
+    PEC_Status_t res;
+
+    LOG_INFO("\n\t PEC_SA_Register \n");
+
+    IDENTIFIER_NOT_USED(InterfaceId);
+
+    DMAHandle1 = Adapter_DMABuf_Handle2DMAResourceHandle(SA_Handle1);
+    DMAHandle2 = Adapter_DMABuf_Handle2DMAResourceHandle(SA_Handle2);
+    DMAHandle3 = Adapter_DMABuf_Handle2DMAResourceHandle(SA_Handle3);
+
+    // The SA, State Record and ARC4 State Record are arrays of uint32_t.
+    // The caller provides them in host-native format.
+    // This function converts them to device-native format
+    // using DMAResource and in-place operations.
+
+    // Endianness conversion for the 1st SA memory block (Main SA Record)
+#ifdef ADAPTER_PEC_ARMRING_ENABLE_SWAP
+    {
+        DMAResource_Record_t * const Rec_p =
+            DMAResource_Handle2RecordPtr(DMAHandle1);
+
+        if (Rec_p == NULL)
+            return PEC_ERROR_INTERNAL;
+
+        DMAResource_SwapEndianness_Set(DMAHandle1, true);
+
+        DMAResource_Write32Array(
+            DMAHandle1,
+            0,
+            Rec_p->Props.Size / 4,
+            Adapter_DMAResource_HostAddr(DMAHandle1));
+    }
+
+    // Endianness conversion for the 2nd SA memory block (State Record)
+    if (DMAHandle2 != NULL)
+    {
+        DMAResource_Record_t * const Rec_p =
+            DMAResource_Handle2RecordPtr(DMAHandle2);
+
+        if (Rec_p == NULL)
+            return PEC_ERROR_INTERNAL;
+
+        // The 2nd SA memory block can never be a subset of
+        // the 1st SA memory block so it is safe to perform
+        // the endianness conversion
+        DMAResource_SwapEndianness_Set(DMAHandle2, true);
+
+        DMAResource_Write32Array(
+            DMAHandle2,
+            0,
+            Rec_p->Props.Size / 4,
+            Adapter_DMAResource_HostAddr(DMAHandle2));
+    }
+
+    // Endianness conversion for the 3d SA memory block (ARC4 State Record)
+    if (DMAHandle3 != NULL)
+    {
+        DMAResource_Record_t * const Rec_p =
+            DMAResource_Handle2RecordPtr(DMAHandle3);
+
+        if (Rec_p == NULL)
+            return PEC_ERROR_INTERNAL;
+
+        // The 3d SA memory block can never be a subset of
+        // the 2nd SA memory block.
+
+        // Check if the 3d SA memory block is not a subset of the 1st one
+        if (!Adapter_DMAResource_IsSubRangeOf(DMAHandle3, DMAHandle1))
+        {
+            // The 3d SA memory block is a separate buffer and does not
+            // overlap with the 1st SA memory block,
+            // so the endianness conversion must be done
+            DMAResource_SwapEndianness_Set(DMAHandle3, true);
+
+            DMAResource_Write32Array(
+                    DMAHandle3,
+                    0,
+                    Rec_p->Props.Size / 4,
+                    Adapter_DMAResource_HostAddr(DMAHandle3));
+        }
+    }
+#endif // ADAPTER_PEC_ARMRING_ENABLE_SWAP
+
+#ifndef ADAPTER_PEC_REMOVE_BOUNCEBUFFERS
+    // Bounce the SA buffers if required
+    // Check if the 3d SA memory block is not a subset of the 1st one
+    if (DMAHandle3 != NULL &&
+        !Adapter_DMAResource_IsSubRangeOf(DMAHandle3, DMAHandle1))
+    {
+        if (!Adapter_PECRegisterSA_BounceIfRequired(&DMAHandle3))
+            return PEC_ERROR_INTERNAL;
+    }
+
+    if (!Adapter_PECRegisterSA_BounceIfRequired(&DMAHandle1))
+        return PEC_ERROR_INTERNAL;
+
+    if (!Adapter_PECRegisterSA_BounceIfRequired(&DMAHandle2))
+        return PEC_ERROR_INTERNAL;
+#endif
+
+    res = Adapter_PECDev_SA_Prepare(SA_Handle1, SA_Handle2, SA_Handle3);
+    if (res != PEC_STATUS_OK)
+    {
+        LOG_WARN(
+            "PEC_SA_Register: "
+            "Adapter_PECDev_PrepareSA returned %d\n",
+            res);
+        return PEC_ERROR_INTERNAL;
+    }
+
+    // now use DMAResource to ensure the engine
+    // can read the memory blocks using DMA
+    DMAResource_PreDMA(DMAHandle1, 0, 0);     // 0,0 = "entire buffer"
+
+    if (DMAHandle2 != NULL)
+        DMAResource_PreDMA(DMAHandle2, 0, 0);
+
+    // Check if the 3d SA memory block is not a subset of the 1st one
+    if (DMAHandle3 != NULL &&
+        !Adapter_DMAResource_IsSubRangeOf(DMAHandle3, DMAHandle1))
+        DMAResource_PreDMA(DMAHandle3, 0, 0);
+
+    LOG_INFO("\n\t PEC_SA_Register done \n");
+
+    return PEC_STATUS_OK;
+}
+
+
+/*----------------------------------------------------------------------------
+ * PEC_SA_UnRegister
+ */
+PEC_Status_t
+PEC_SA_UnRegister(
+        const unsigned int InterfaceId,
+        DMABuf_Handle_t SA_Handle1,
+        DMABuf_Handle_t SA_Handle2,
+        DMABuf_Handle_t SA_Handle3)
+{
+    DMAResource_Handle_t SA_Handle[3];
+    PEC_Status_t res;
+    int i, MaxHandles;
+
+    LOG_INFO("\n\t PEC_SA_UnRegister \n");
+
+    IDENTIFIER_NOT_USED(InterfaceId);
+
+    res = Adapter_PECDev_SA_Remove(SA_Handle1, SA_Handle2, SA_Handle3);
+    if (res != PEC_STATUS_OK)
+    {
+        LOG_CRIT(
+            "PEC_SA_UnRegister: "
+            "Adapter_PECDev_SA_Remove returned %d\n",
+            res);
+        return PEC_ERROR_INTERNAL;
+    }
+
+    SA_Handle[0] = Adapter_DMABuf_Handle2DMAResourceHandle(SA_Handle1);
+    SA_Handle[1] = Adapter_DMABuf_Handle2DMAResourceHandle(SA_Handle2);
+    SA_Handle[2] = Adapter_DMABuf_Handle2DMAResourceHandle(SA_Handle3);
+
+    // Check if the 3d SA memory block is not a subset of the 1st one
+    if (SA_Handle[0] != NULL &&
+        SA_Handle[2] != NULL &&
+        Adapter_DMAResource_IsSubRangeOf(SA_Handle[2], SA_Handle[0]))
+        MaxHandles = 2;
+    else
+        MaxHandles = 3;
+
+    for (i = 0; i < MaxHandles; i++)
+    {
+        if (DMAResource_IsValidHandle(SA_Handle[i]))
+        {
+            DMAResource_Handle_t DMAHandle = SA_Handle[i];
+            void *HostAddr;
+            DMAResource_Record_t * Rec_p =
+                DMAResource_Handle2RecordPtr(DMAHandle);
+
+            // Check if a bounce buffer is in use
+#ifndef ADAPTER_PEC_REMOVE_BOUNCEBUFFERS
+            void * OrigHostAddr;
+            DMAResource_Record_t * HostRec_p = Rec_p;
+
+            OrigHostAddr = Adapter_DMAResource_HostAddr(DMAHandle);
+
+            if (Adapter_DMAResource_IsForeignAllocated(SA_Handle[i]))
+            {
+                // Get bounce buffer handle and its record
+                DMAHandle = HostRec_p->bounce.Bounce_Handle;
+                Rec_p = DMAResource_Handle2RecordPtr(DMAHandle);
+            }
+#endif /* ADAPTER_PEC_REMOVE_BOUNCEBUFFERS */
+
+            HostAddr = Adapter_DMAResource_HostAddr(DMAHandle);
+            // ensure we look at valid engine-written data
+            // 0,0 = "entire buffer"
+            DMAResource_PostDMA(DMAHandle, 0, 0);
+
+            // convert to host format
+            if (Rec_p != NULL)
+                DMAResource_Read32Array(
+                    DMAHandle,
+                    0,
+                    Rec_p->Props.Size / 4,
+                    HostAddr);
+
+            // copy from bounce buffer to original buffer
+#ifndef ADAPTER_PEC_REMOVE_BOUNCEBUFFERS
+            if (Adapter_DMAResource_IsForeignAllocated(SA_Handle[i]) &&
+                HostRec_p != NULL)
+            {
+                // copy the data from bounce to original buffer
+                memcpy(
+                    OrigHostAddr,
+                    HostAddr,
+                    HostRec_p->Props.Size);
+
+                // free the bounce handle
+                DMAResource_Release(HostRec_p->bounce.Bounce_Handle);
+                HostRec_p->bounce.Bounce_Handle = NULL;
+            }
+#endif /* ADAPTER_PEC_REMOVE_BOUNCEBUFFERS */
+        } // if handle valid
+    } // for
+
+    LOG_INFO("\n\t PEC_SA_UnRegister done\n");
+
+    return PEC_STATUS_OK;
+}
+
+
+/*----------------------------------------------------------------------------
+ * PEC_Packet_Put
+ */
+PEC_Status_t
+PEC_Packet_Put(
+        const unsigned int InterfaceId,
+        const PEC_CommandDescriptor_t * Commands_p,
+        const unsigned int CommandsCount,
+        unsigned int * const PutCount_p)
+{
+    unsigned int CmdLp;
+    unsigned int PktCnt;
+    unsigned int CmdDescriptorCount;
+    PEC_Status_t res = 0, res2, PEC_Rc = PEC_STATUS_OK;
+    unsigned int FreeSlots;
+
+    LOG_INFO("\n\t PEC_Packet_Put \n");
+
+    if (InterfaceId >= ADAPTER_PEC_DEVICE_COUNT)
+        return PEC_ERROR_BAD_PARAMETER;
+
+#ifdef ADAPTER_PEC_STRICT_ARGS
+    if (Commands_p == NULL ||
+        CommandsCount == 0 ||
+        PutCount_p == NULL)
+    {
+        return PEC_ERROR_BAD_PARAMETER;
+    }
+#endif
+
+    // initialize the output parameters
+    *PutCount_p = 0;
+
+#ifdef ADAPTER_PEC_STRICT_ARGS
+    // validate the descriptors
+    // (error out before bounce buffer allocation)
+    for (CmdLp = 0; CmdLp < CommandsCount; CmdLp++)
+        if (Commands_p[CmdLp].Bypass_WordCount > 255)
+            return PEC_ERROR_BAD_PARAMETER;
+#endif /* ADAPTER_PEC_STRICT_ARGS */
+
+    if (!Adapter_Lock_CS_Enter(&AdapterPEC_PutCS[InterfaceId]))
+        return PEC_STATUS_BUSY;
+
+    if (!PEC_IsInitialized[InterfaceId])
+    {
+        Adapter_Lock_CS_Leave(&AdapterPEC_PutCS[InterfaceId]);
+        return PEC_ERROR_BAD_USE_ORDER;
+    }
+
+    CmdDescriptorCount = MIN(ADAPTER_PEC_MAX_LOGICDESCR, CommandsCount);
+    FreeSlots = 0;
+    CmdLp = 0;
+    while (CmdLp < CmdDescriptorCount)
+    {
+        unsigned int j;
+        unsigned int count;
+        unsigned int NonSGPackets;
+
+#ifndef ADAPTER_PEC_ENABLE_SCATTERGATHER
+        NonSGPackets = CmdDescriptorCount - CmdLp;
+        // All remaining packets are non-SG.
+#else
+        unsigned int GatherParticles;
+        unsigned int ScatterParticles;
+        unsigned int i;
+
+        for (i = CmdLp; i < CmdDescriptorCount; i++)
+        {
+            PEC_SGList_GetCapacity(Commands_p[i].SrcPkt_Handle,
+                                   &GatherParticles);
+            if (PEC_ContinuousScatter[InterfaceId])
+                ScatterParticles = 0;
+            else
+                PEC_SGList_GetCapacity(Commands_p[i].DstPkt_Handle,
+                                       &ScatterParticles);
+            if ( GatherParticles > 0 || ScatterParticles > 0)
+                break;
+        }
+        NonSGPackets = i - CmdLp;
+
+        if (NonSGPackets == 0)
+        {
+            bool fSuccess;
+
+            if (RPM_DEVICE_IO_START_MACRO(ADAPTER_PEC_RPM_EIP202_DEVICE0_ID +
+                                                                   InterfaceId,
+                                          RPM_FLAG_SYNC) != RPM_SUCCESS)
+            {
+                PEC_Rc = PEC_ERROR_INTERNAL;
+                break;
+            }
+
+            // First packet found is scatter gather.
+            fSuccess = Adapter_PECDev_TestSG(InterfaceId,
+                                             GatherParticles,
+                                             ScatterParticles);
+
+            (void)RPM_DEVICE_IO_STOP_MACRO(ADAPTER_PEC_RPM_EIP202_DEVICE0_ID +
+                                                           InterfaceId,
+                                           RPM_FLAG_ASYNC);
+
+            if (!fSuccess)
+            {
+                PEC_Rc = PEC_ERROR_INTERNAL;
+                break;
+            }
+
+            // Process a single SG packet in this iteration.
+            FreeSlots = 1;
+        }
+        else
+#endif
+        if (FreeSlots == 0)
+        {
+            if (RPM_DEVICE_IO_START_MACRO(ADAPTER_PEC_RPM_EIP202_DEVICE0_ID +
+                                                                   InterfaceId,
+                                          RPM_FLAG_SYNC) != RPM_SUCCESS)
+            {
+                PEC_Rc = PEC_ERROR_INTERNAL;
+                break;
+            }
+
+            // Allow all non-SG packets to be processed in this iteration,
+            // but limited by the number of free slots in the ring(s).
+            FreeSlots = Adapter_PECDev_GetFreeSpace(InterfaceId);
+
+            (void)RPM_DEVICE_IO_STOP_MACRO(ADAPTER_PEC_RPM_EIP202_DEVICE0_ID +
+                                                           InterfaceId,
+                                           RPM_FLAG_ASYNC);
+
+            if (FreeSlots > NonSGPackets)
+                FreeSlots = NonSGPackets;
+
+            if (FreeSlots == 0)
+                break;
+        }
+
+        for (PktCnt=0; PktCnt<FreeSlots; PktCnt++)
+        {
+            res = Adapter_Packet_Prepare(InterfaceId,
+                                         Commands_p + CmdLp + PktCnt);
+            if (res != PEC_STATUS_OK)
+            {
+                LOG_CRIT("%s: Adapter_Packet_Prepare error %d\n", __func__, res);
+                PEC_Rc = res;
+                break;
+            }
+
+            if (!PEC_ContinuousScatter[InterfaceId])
+            {
+                Adapter_FIFO_Put(&(Adapter_SideChannelFIFO[InterfaceId]),
+                                 Commands_p[CmdLp+PktCnt].User_p,
+                                 Commands_p[CmdLp+PktCnt].SrcPkt_Handle,
+                                 Commands_p[CmdLp+PktCnt].DstPkt_Handle,
+                                 Commands_p[CmdLp+PktCnt].Token_Handle,
+                                 Commands_p[CmdLp+PktCnt].Bypass_WordCount);
+            }
+        }
+
+        // RPM_Device_IO_Start() must be called for each successfully submitted
+        // packet
+        for (j = 0; j < PktCnt; j++)
+        {
+            // Skipped error checking to reduce code complexity
+            (void)RPM_DEVICE_IO_START_MACRO(ADAPTER_PEC_RPM_EIP202_DEVICE0_ID +
+                                                                InterfaceId,
+                                            RPM_FLAG_SYNC);
+        }
+
+        res2 = Adapter_PECDev_Packet_Put(InterfaceId,
+                                         Commands_p + CmdLp,
+                                         PktCnt,
+                                         &count);
+        if (res2 != PEC_STATUS_OK)
+        {
+            LOG_CRIT("%s: Adapter_PECDev_Packet_Put error %d\n", __func__, res2);
+            PEC_Rc = res2;
+        }
+
+        FreeSlots -= count;
+        *PutCount_p += count;
+
+        if (count <  PktCnt)
+        {
+            LOG_WARN("PEC_Packet_Put: withdrawing %d prepared packets\n",
+                     PktCnt - count);
+
+            for (j = count; j < PktCnt; j++)
+            {
+                if (!PEC_ContinuousScatter[InterfaceId])
+                {
+                    Adapter_FIFO_Withdraw(&(Adapter_SideChannelFIFO[InterfaceId]));
+                    Adapter_Packet_Finalize(Commands_p[CmdLp + j].SrcPkt_Handle,
+                                            Commands_p[CmdLp + j].DstPkt_Handle,
+                                            Commands_p[CmdLp + j].Token_Handle);
+                }
+
+                // RPM_DEVICE_IO_STOP_MACRO() must be called here for packets
+                // which could not be successfully submitted,
+                // for example because device queue was full
+                (void)RPM_DEVICE_IO_STOP_MACRO(ADAPTER_PEC_RPM_EIP202_DEVICE0_ID +
+                                                               InterfaceId,
+                                               RPM_FLAG_ASYNC);
+            }
+            break;
+        }
+
+        CmdLp += count;
+
+        if (res != PEC_STATUS_OK || res2 != PEC_STATUS_OK)
+        {
+            PEC_Rc = PEC_ERROR_INTERNAL;
+            break;
+        }
+    } // while
+
+    LOG_INFO("\n\t PEC_Packet_Put done \n");
+
+    Adapter_Lock_CS_Leave(&AdapterPEC_PutCS[InterfaceId]);
+
+    return PEC_Rc;
+}
+
+
+/*----------------------------------------------------------------------------
+ * PEC_Packet_Get
+ */
+PEC_Status_t
+PEC_Packet_Get(
+        const unsigned int InterfaceId,
+        PEC_ResultDescriptor_t * Results_p,
+        const unsigned int ResultsLimit,
+        unsigned int * const GetCount_p)
+{
+    LOG_INFO("\n\t PEC_Packet_Get \n");
+
+    if (InterfaceId >= ADAPTER_PEC_DEVICE_COUNT)
+        return PEC_ERROR_BAD_PARAMETER;
+
+#ifdef ADAPTER_PEC_STRICT_ARGS
+    if (Results_p == NULL ||
+        GetCount_p == NULL ||
+        ResultsLimit == 0)
+    {
+        return PEC_ERROR_BAD_PARAMETER;
+    }
+#endif
+
+    // initialize the output parameter
+    *GetCount_p = 0;
+
+    if (!Adapter_Lock_CS_Enter(&AdapterPEC_GetCS[InterfaceId]))
+        return PEC_STATUS_BUSY;
+
+    if (!PEC_IsInitialized[InterfaceId])
+    {
+        Adapter_Lock_CS_Leave(&AdapterPEC_GetCS[InterfaceId]);
+        return PEC_ERROR_BAD_USE_ORDER;
+    }
+
+    // read descriptors from PEC device
+    {
+        PEC_Status_t res;
+        unsigned int ResLp;
+        unsigned int Limit = MIN(ResultsLimit, ADAPTER_PEC_MAX_LOGICDESCR);
+        unsigned int count;
+        DMABuf_Handle_t Token_Handle = DMABuf_NULLHandle;
+
+        res=Adapter_PECDev_Packet_Get(InterfaceId,
+                                      Results_p,
+                                      Limit,
+                                      &count);
+        if (res != PEC_STATUS_OK)
+        {
+            LOG_CRIT("PEC_Packet_Get() returned error: %d\n", res);
+            Adapter_Lock_CS_Leave(&AdapterPEC_GetCS[InterfaceId]);
+            return res;
+        }
+
+        for (ResLp = 0; ResLp < count; ResLp++)
+        {
+            // To help CommandNotifyCB
+            if (ResLp == count-1)
+                Adapter_MakeCommandNotify_CallBack(InterfaceId);
+
+            (void)RPM_DEVICE_IO_STOP_MACRO(ADAPTER_PEC_RPM_EIP202_DEVICE0_ID +
+                                                        InterfaceId,
+                                           RPM_FLAG_ASYNC);
+            if (PEC_ContinuousScatter[InterfaceId])
+            {
+                unsigned int i;
+                DMABuf_Handle_t DestHandle = DMABuf_NULLHandle;
+                DMAResource_Handle_t DMARes_Handle;
+                if (Results_p[ResLp].NumParticles == 1)
+                {
+                    /* No real scatter, can fixup using single destination
+                       handle */
+                    Adapter_FIFO_Get(&Adapter_SideChannelFIFO[InterfaceId],
+                                     NULL,
+                                     NULL,
+                                     &DestHandle,
+                                     NULL,
+                                     NULL);
+                    DMARes_Handle =
+                        Adapter_DMABuf_Handle2DMAResourceHandle(DestHandle);
+                    DMAResource_PostDMA(DMARes_Handle, 0, 0);
+#ifdef ADAPTER_AUTO_FIXUP
+                    IOToken_Fixup(Results_p[ResLp].OutputToken_p,
+                                  DestHandle);
+#endif
+                    Results_p[ResLp].User_p = NULL;
+                    Results_p[ResLp].SrcPkt_Handle = DMABuf_NULLHandle;
+                    Results_p[ResLp].DstPkt_Handle = DestHandle;
+                    Results_p[ResLp].Bypass_WordCount = 0;
+                    if (!DMABuf_Handle_IsSame(&Results_p[ResLp].DstPkt_Handle,
+                                              &DMABuf_NULLHandle))
+                    {
+                        Results_p[ResLp].DstPkt_p =
+                            Adapter_DMAResource_HostAddr(
+                                Adapter_DMABuf_Handle2DMAResourceHandle(
+                                    Results_p[ResLp].DstPkt_Handle));
+                    }
+                    else
+                    {
+                        Results_p[ResLp].DstPkt_p = NULL;
+                    }
+                }
+                else
+                {
+#if defined(ADAPTER_PEC_ENABLE_SCATTERGATHER) && defined(ADAPTER_AUTO_FIXUP)
+                    DMAResource_Record_t * Rec_p;
+                    PEC_Status_t PEC_Rc;
+                    DMABuf_Handle_t Fixup_SGList; /* scatter list for Fixup in continuous scatter mode */
+                    PEC_Rc = PEC_SGList_Create(Results_p[ResLp].NumParticles,
+                                               &Fixup_SGList);
+#endif
+                    for (i = 0; i < Results_p[ResLp].NumParticles; i++)
+                    {
+                        Adapter_FIFO_Get(&Adapter_SideChannelFIFO[InterfaceId],
+                                         NULL,
+                                         NULL,
+                                         &DestHandle,
+                                         NULL,
+                                     NULL);
+                        DMARes_Handle =
+                            Adapter_DMABuf_Handle2DMAResourceHandle(DestHandle);
+                        DMAResource_PostDMA(DMARes_Handle, 0, 0);
+#if defined(ADAPTER_PEC_ENABLE_SCATTERGATHER) && defined(ADAPTER_AUTO_FIXUP)
+                        if (PEC_Rc == PEC_STATUS_OK)
+                        {
+                            Rec_p = DMAResource_Handle2RecordPtr(DMARes_Handle);
+
+                            PEC_SGList_Write(Fixup_SGList,
+                                             i,
+                                             DestHandle,
+                                             Rec_p->Props.Size);
+                        }
+#endif
+                    }
+#if defined(ADAPTER_PEC_ENABLE_SCATTERGATHER) && defined(ADAPTER_AUTO_FIXUP)
+                    if (PEC_Rc == PEC_STATUS_OK)
+                    {
+                        IOToken_Fixup(Results_p[ResLp].OutputToken_p,
+                              Fixup_SGList);
+                        PEC_SGList_Destroy(Fixup_SGList);
+                    }
+
+#endif
+                    Results_p[ResLp].User_p = NULL;
+                    Results_p[ResLp].SrcPkt_Handle = DMABuf_NULLHandle;
+                    Results_p[ResLp].DstPkt_Handle = DMABuf_NULLHandle;
+                    Results_p[ResLp].Bypass_WordCount = 0;
+                }
+            }
+            else
+            {
+                Adapter_FIFO_Get(&(Adapter_SideChannelFIFO[InterfaceId]),
+                                 &(Results_p[ResLp].User_p),
+                                 &(Results_p[ResLp].SrcPkt_Handle),
+                                 &(Results_p[ResLp].DstPkt_Handle),
+                                 &Token_Handle,
+                                 &(Results_p[ResLp].Bypass_WordCount));
+
+                Adapter_Packet_Finalize(Results_p[ResLp].SrcPkt_Handle,
+                                    Results_p[ResLp].DstPkt_Handle,
+                                    Token_Handle);
+#ifdef ADAPTER_AUTO_FIXUP
+                IOToken_Fixup(Results_p[ResLp].OutputToken_p,
+                              Results_p[ResLp].DstPkt_Handle);
+#endif
+
+                if (!DMABuf_Handle_IsSame(&Results_p[ResLp].DstPkt_Handle,
+                                          &DMABuf_NULLHandle))
+                {
+                    Results_p[ResLp].DstPkt_p =
+                        Adapter_DMAResource_HostAddr(
+                            Adapter_DMABuf_Handle2DMAResourceHandle(
+                                Results_p[ResLp].DstPkt_Handle));
+                }
+                else
+                {
+                    Results_p[ResLp].DstPkt_p = NULL;
+                }
+            }
+            *GetCount_p += 1;
+        } // for
+    }
+
+    LOG_INFO("\n\t PEC_Packet_Get done \n");
+
+    Adapter_Lock_CS_Leave(&AdapterPEC_GetCS[InterfaceId]);
+
+    return PEC_STATUS_OK;
+}
+
+
+/*----------------------------------------------------------------------------
+ * PEC_CD_Control_Write
+ *
+ * Write the Control1 and Control2 engine-specific fields in the
+ * Command Descriptor The other fields (such as SrcPkt_ByteCount and
+ * Bypass_WordCount must have been filled in already.
+ *
+ * Command_p (input, output)
+ *     Command descriptor whose Control1 and Control2 fields must be filled in.
+ *
+ * PacketParams_p (input)
+ *     Per-packet parameters.
+ *
+ * This function is not implemented for all engine types.
+ */
+PEC_Status_t
+PEC_CD_Control_Write(
+    PEC_CommandDescriptor_t *Command_p,
+    const PEC_PacketParams_t *PacketParams_p)
+{
+    return Adapter_PECDev_CD_Control_Write(Command_p, PacketParams_p);
+}
+
+
+/*----------------------------------------------------------------------------
+ * PEC_RD_Status_Read
+ */
+PEC_Status_t
+PEC_RD_Status_Read(
+        const PEC_ResultDescriptor_t * const Result_p,
+        PEC_ResultStatus_t * const ResultStatus_p)
+{
+    return Adapter_PECDev_RD_Status_Read(Result_p, ResultStatus_p);
+}
+
+
+/*----------------------------------------------------------------------------
+ * PEC_CommandNotify_Request
+ */
+PEC_Status_t
+PEC_CommandNotify_Request(
+        const unsigned int InterfaceId,
+        PEC_NotifyFunction_t CBFunc_p,
+        const unsigned int CommandsCount)
+{
+    unsigned int PacketSlotsEmptyCount;
+
+    LOG_INFO("\n\t PEC_CommandNotify_Request \n");
+
+    if (InterfaceId >= ADAPTER_PEC_DEVICE_COUNT)
+        return PEC_ERROR_BAD_PARAMETER;
+
+    if (CBFunc_p == NULL ||
+        CommandsCount == 0 ||
+        CommandsCount > ADAPTER_PEC_MAX_PACKETS)
+    {
+        return PEC_ERROR_BAD_PARAMETER;
+    }
+
+    if (!PEC_IsInitialized[InterfaceId])
+        return PEC_ERROR_BAD_USE_ORDER;
+
+    if (RPM_DEVICE_IO_START_MACRO(ADAPTER_PEC_RPM_EIP202_DEVICE0_ID + InterfaceId,
+                                  RPM_FLAG_SYNC) != RPM_SUCCESS)
+        return PEC_ERROR_INTERNAL;
+
+    PacketSlotsEmptyCount = Adapter_PECDev_GetFreeSpace(InterfaceId);
+
+    (void)RPM_DEVICE_IO_STOP_MACRO(ADAPTER_PEC_RPM_EIP202_DEVICE0_ID + InterfaceId,
+                                   RPM_FLAG_ASYNC);
+
+    if (CommandsCount <= PacketSlotsEmptyCount)
+    {
+        LOG_INFO(
+            "PEC_CommandNotify_Request: "
+            "Invoking command notify callback immediately\n");
+
+        CBFunc_p();
+    }
+    else
+    {
+        PEC_Notify[InterfaceId].CommandsCount = CommandsCount;
+        PEC_Notify[InterfaceId].CommandNotifyCB_p = CBFunc_p;
+
+#ifdef ADAPTER_PEC_INTERRUPTS_ENABLE
+        if (RPM_DEVICE_IO_START_MACRO(ADAPTER_PEC_RPM_EIP202_DEVICE0_ID + InterfaceId,
+                                      RPM_FLAG_SYNC) != RPM_SUCCESS)
+            return PEC_ERROR_INTERNAL;
+
+        /* Note that space for new commands may have become available before
+         * the call to PEC_CommandNotify_Request and the associated interrupt
+         * may already be pending. In this case the interrupt will occur
+         * immediately.
+         */
+        Adapter_PECDev_Enable_CommandIRQ(InterfaceId);
+
+        (void)RPM_DEVICE_IO_STOP_MACRO(ADAPTER_PEC_RPM_EIP202_DEVICE0_ID + InterfaceId,
+                                       RPM_FLAG_ASYNC);
+#endif /* ADAPTER_PEC_INTERRUPTS_ENABLE */
+    }
+
+    LOG_INFO("\n\t PEC_CommandNotify_Request done \n");
+
+    return PEC_STATUS_OK;
+}
+
+
+/*----------------------------------------------------------------------------
+ * PEC_ResultNotify_Request
+ */
+PEC_Status_t
+PEC_ResultNotify_Request(
+        const unsigned int InterfaceId,
+        PEC_NotifyFunction_t CBFunc_p,
+        const unsigned int ResultsCount)
+{
+    LOG_INFO("\n\t PEC_ResultNotify_Request \n");
+
+    if (InterfaceId >= ADAPTER_PEC_DEVICE_COUNT)
+        return PEC_ERROR_BAD_PARAMETER;
+
+    if (CBFunc_p == NULL ||
+        ResultsCount == 0 ||
+        ResultsCount > ADAPTER_PEC_MAX_PACKETS)
+    {
+        return PEC_ERROR_BAD_PARAMETER;
+    }
+
+    if (!PEC_IsInitialized[InterfaceId])
+        return PEC_ERROR_BAD_USE_ORDER;
+
+    // install it
+    PEC_Notify[InterfaceId].ResultsCount = ResultsCount;
+    PEC_Notify[InterfaceId].ResultNotifyCB_p = CBFunc_p;
+
+#ifdef ADAPTER_PEC_INTERRUPTS_ENABLE
+    if (RPM_DEVICE_IO_START_MACRO(ADAPTER_PEC_RPM_EIP202_DEVICE0_ID + InterfaceId,
+                                  RPM_FLAG_SYNC) != RPM_SUCCESS)
+        return PEC_ERROR_INTERNAL;
+
+    /* Note that results may have become available before the call
+       to PEC_ResultNotify_Request and the associated interrupts may already
+       be pending. In this case the interrupt will occur immediately.
+     */
+    Adapter_PECDev_Enable_ResultIRQ(InterfaceId);
+
+    (void)RPM_DEVICE_IO_STOP_MACRO(ADAPTER_PEC_RPM_EIP202_DEVICE0_ID + InterfaceId,
+                                   RPM_FLAG_ASYNC);
+#endif /* ADAPTER_PEC_INTERRUPTS_ENABLE */
+
+    LOG_INFO("\n\t PEC_ResultNotify_Request done\n");
+
+    return PEC_STATUS_OK;
+}
+
+
+/*----------------------------------------------------------------------------
+ * PEC_Scatter_Preload
+ */
+PEC_Status_t
+PEC_Scatter_Preload(
+        const unsigned int InterfaceId,
+        DMABuf_Handle_t * Handles_p,
+        const unsigned int HandlesCount,
+        unsigned int * const AcceptedCount_p)
+{
+    PEC_Status_t rc;
+    unsigned int i;
+    unsigned int HandlesToUse,ri,wi;
+        LOG_INFO("\n\t PEC_Scatter_Preload\n");
+
+    if (InterfaceId >= ADAPTER_PEC_DEVICE_COUNT)
+        return PEC_ERROR_BAD_PARAMETER;
+
+    if (!Adapter_Lock_CS_Enter(&AdapterPEC_PutCS[InterfaceId]))
+        return PEC_STATUS_BUSY;
+
+    if (!PEC_IsInitialized[InterfaceId] || !PEC_ContinuousScatter[InterfaceId])
+    {
+        Adapter_Lock_CS_Leave(&AdapterPEC_PutCS[InterfaceId]);
+        return PEC_ERROR_BAD_USE_ORDER;
+    }
+    ri = Adapter_SideChannelFIFO[InterfaceId].ReadIndex;
+    wi = Adapter_SideChannelFIFO[InterfaceId].WriteIndex;
+    // Compute the number of free slots in the ring from the read/write index
+    // of the side channel FIFO (which is larger than the ring)
+    if (wi >= ri)
+        HandlesToUse = ADAPTER_PEC_MAX_PACKETS - 1 - wi + ri ;
+    else
+        HandlesToUse = ADAPTER_PEC_MAX_PACKETS - 1
+            - Adapter_SideChannelFIFO[InterfaceId].Size - wi + ri;
+    if (HandlesToUse > ADAPTER_PEC_MAX_LOGICDESCR)
+        HandlesToUse = ADAPTER_PEC_MAX_LOGICDESCR;
+    if (HandlesToUse > HandlesCount)
+        HandlesToUse = HandlesCount;
+    for (i=0; i < HandlesToUse; i++)
+    {
+        DMAResource_Handle_t DMARes_Handle =
+            Adapter_DMABuf_Handle2DMAResourceHandle(Handles_p[i]);
+        DMAResource_PreDMA(DMARes_Handle, 0, 0);
+        Adapter_FIFO_Put(&Adapter_SideChannelFIFO[InterfaceId],
+                         NULL,
+                         DMABuf_NULLHandle,
+                         Handles_p[i],
+                         DMABuf_NULLHandle,
+                         0);
+    }
+    rc = Adapter_PECDev_Scatter_Preload(InterfaceId,
+                                        Handles_p,
+                                        HandlesToUse);
+
+    *AcceptedCount_p = HandlesToUse;
+    Adapter_Lock_CS_Leave(&AdapterPEC_PutCS[InterfaceId]);
+    return rc;
+}
+
+
+/*----------------------------------------------------------------------------
+ * PEC_Scatter_PreloadNotify_Request
+ */
+PEC_Status_t
+PEC_Scatter_PreloadNotify_Request(
+        const unsigned int InterfaceId,
+        PEC_NotifyFunction_t CBFunc_p,
+        const unsigned int ConsumedCount)
+{
+    IDENTIFIER_NOT_USED(InterfaceId);
+    IDENTIFIER_NOT_USED(CBFunc_p);
+    IDENTIFIER_NOT_USED(ConsumedCount);
+
+    return PEC_ERROR_NOT_IMPLEMENTED;
+}
+
+
+/*----------------------------------------------------------------------------
+ * PEC_Put_Dump
+ */
+void
+PEC_Put_Dump(
+        const unsigned int InterfaceId,
+        const unsigned int FirstSlotId,
+        const unsigned int LastSlotId,
+        const bool fDumpRDRAdmin,
+        const bool fDumpRDRCache)
+{
+    Adapter_PECDev_Put_Dump(InterfaceId,
+                            FirstSlotId,
+                            LastSlotId,
+                            fDumpRDRAdmin,
+                            fDumpRDRCache);
+}
+
+
+
+
+/*----------------------------------------------------------------------------
+ * PEC_Get_Dump
+ */
+void
+PEC_Get_Dump(
+        const unsigned int InterfaceId,
+        const unsigned int FirstSlotId,
+        const unsigned int LastSlotId,
+        const bool fDumpRDRAdmin,
+        const bool fDumpRDRCache)
+{
+    Adapter_PECDev_Get_Dump(InterfaceId,
+                            FirstSlotId,
+                            LastSlotId,
+                            fDumpRDRAdmin,
+                            fDumpRDRCache);
+}
+
+
+#endif /* ADAPTER_PE_MODE_DHM */
+
+
+/* end of file adapter_pec_dma.c */