/*!
 *  @brief      Function to Map Host processor to Remote processors
 *              module
 *
 *              This function can be called by the application to map their
 *              address space to remote slave's address space.
 *
 *  @param      MpuAddr
 *
 *  @sa         SysLinkMemUtils_unmap
 */
Int
SysLinkMemUtils_map (SyslinkMemUtils_MpuAddrToMap   mpuAddrList [],
                     UInt32                         numOfBuffers,
                     UInt32 *                       mappedAddr,
                     ProcMgr_MapType                memType,
                     ProcMgr_ProcId                 procId)
{
    ProcMgr_Handle  procMgrHandle;
    UInt32          mappedSize;
    Int32           status = PROCMGR_SUCCESS;

    if (numOfBuffers > 1) {
        status = PROCMGR_E_INVALIDARG;
        Osal_printf ("SysLinkMemUtils_map numBufError [0x%x]\n", status);
        return status;
    }

    if (procId == PROC_APPM3) {
        procId = PROC_SYSM3;
    }

    if (memType == ProcMgr_MapType_Tiler) {
        /* TILER addresses are pre-mapped, so just return the TILER ssPtr */
        *mappedAddr = TilerMem_VirtToPhys ((Void *)mpuAddrList [0].mpuAddr);
        return status;
    }

    /* Open a handle to the ProcMgr instance. */
    status = ProcMgr_open (&procMgrHandle, procId);
    if (status < 0) {
        Osal_printf ("Error in ProcMgr_open [0x%x]\n", status);
    }
    else {
        /* FIX ME: Add Proc reserve call */
        status = ProcMgr_map (procMgrHandle, (UInt32)mpuAddrList [0].mpuAddr,
                        (UInt32)mpuAddrList [0].size, mappedAddr, &mappedSize,
                        memType, procId);
         /* FIX ME: Add the table that keeps the C-D translations */
        if (status < 0) {
            Osal_printf ("Error in ProcMgr_map [0x%x]\n", status);
        }
        else {
            status = ProcMgr_close (&procMgrHandle);
        }
    }

    return status;
}
/*
 *  ======== procCreate ========
 */
static Bool procCreate(Processor_Handle proc)
{
    Int status = 0;
    Bool retVal;
    ProcMgr_AttachParams attachParams;
    ProcMgr_StartParams  startParams;
    ProcMgr_State        state;
    ProcMgr_AddrInfo     CMEMAddrInfo;
    HeapBufMP_Params heapP;
    CMEM_BlockAttrs cmemBlockAttrs;
    Int blockNum;
    Int nCMEMBlocks;
    Bool createAndRegisterHeap;
    Int16 heapId;
    UInt16 regionId;
    UInt32 numMsgs;
    UInt32 msgSize;
    Char heapName[32];

    Log_print1(Diags_ENTRY, "[+E] Processor_create_d> Enter(proc=0x%x)",
            (IArg)proc);

    /* Create and initialize the PROC object */
    Log_print1(Diags_USER2, "[+2] Processor_create_d> "
    "Retrieving CPU ID for '%s'...", (IArg)(proc->attrs.cpuId));

    proc->cpuId = Processor_getCoreId(proc->attrs.cpuId);
    if (proc->cpuId < 0) {
        Log_print1(Diags_USER7, "[+7] Processor_create_d> "
               "Processor_getCoreId() failed: %d", proc->cpuId);
        goto fail;
    }

    /* Open DSP ProcMgr */
    Log_print2(Diags_USER2, "[+2] Processor_create_d> "
           "Opening %s ProcMgr for cpuId %d...",
           (IArg)proc->attrs.cpuId, proc->cpuId);
    status = ProcMgr_open(&proc->procMgrH, proc->cpuId);

    if (status < 0) {
        Log_print1(Diags_USER7, "[+7] Processor_create_d> "
               "ProcMgr_open() failed: %d", (IArg)status);
        goto fail;
    }

    /* Attach the DSP */
    Log_print1(Diags_USER2, "[+2] Processor_create_d> "
           "Attaching to %s...", (IArg)proc->attrs.cpuId);

    if (proc->useExtLoader == FALSE) {
        /* We load the slave */
        ProcMgr_getAttachParams(NULL, &attachParams);
        status = ProcMgr_attach(proc->procMgrH, &attachParams);
        if (status < 0) {
            Log_print1(Diags_USER7, "[+7] Processor_create_d> "
                    "ProcMgr_attach() failed: %d", (IArg)status);
            goto fail;
        }

        /* Map slave memory */
        if (!mapByFile(proc->procMgrH, proc->memMapName, proc->cpuId, TRUE)) {
            Log_print0(Diags_USER6, "Processor_create_d> mapByFile() failed!");
        }

        /* Load the executable on the DSP */
        Log_print3(Diags_USER2, "[+2] Processor_create_d> "
                "Loading %s on %s (%d args)...",
                (IArg)(proc->imageName), (IArg)(proc->attrs.cpuId),
                (IArg)(proc->attrs.argc));

        status = ProcMgr_load(proc->procMgrH, proc->imageName,
                proc->attrs.argc, proc->attrs.argv, NULL, &proc->fileId);
        if (status < 0) {
            Log_print1(Diags_USER7, "[+7] Processor_create_d> "
                    "ProcMgr_load() failed: %d", status);
            goto fail;
        }

        /* temporary: to be done by SysLink in the future */
        Log_print0(Diags_USER1, "[+2] Processor_create_d> "
                "calling Ipc_control(LOADCALLBACK)...");
        status = Ipc_control(proc->cpuId, Ipc_CONTROLCMD_LOADCALLBACK, NULL);
        Log_print1(Diags_USER1, "[+2] Processor_create_d> "
                "Ipc_control(LOADCALLBACK) status: %d", (IArg)status);

        /* Start execution on DSP */
        Log_print1(Diags_USER2, "[+2] Processor_create_d> Starting %s ...",
                (IArg)proc->attrs.cpuId);

        ProcMgr_getStartParams(proc->procMgrH, &startParams);
        status = ProcMgr_start(proc->procMgrH, &startParams);
        if (status < 0) {
            Log_print1(Diags_USER7, "Processor_create_d> "
                    "ProcMgr_start() failed: %d", status);
            goto fail;
        }
    } // if (proc->useExtLoader == FALSE)
    else {
        /* Check the state of the processor to make sure it's really running */
        state = ProcMgr_getState(proc->procMgrH);
        if (state != ProcMgr_State_Running) {
            Log_print1(Diags_USER7, "Processor_create_d> Invalid processor "
                    "state [%d].", state);
            goto fail;
        }

        Log_print0(Diags_USER1, "[+2] Processor_create_d> "
                "calling Ipc_control(LOADCALLBACK)...");
        status = Ipc_control(proc->cpuId, Ipc_CONTROLCMD_LOADCALLBACK, NULL);
        proc->loadCallBackStatus = status;
        Log_print1(Diags_USER1, "[+2] Processor_create_d> "
                "Ipc_control(LOADCALLBACK) status: %d", (IArg)status);

        if (status < 0) {
            Log_print1(Diags_USER7, "Processor_create_d> "
                    "Ipc_control(LOADCALLBACK) failed: %d", status);
            goto fail;
        }
    }

    status = Ipc_control(proc->cpuId, Ipc_CONTROLCMD_STARTCALLBACK, NULL);
    proc->startCallBackStatus = status;
    Log_print1(Diags_USER1, "[+2] Processor_create_d> "
            "Ipc_control(STARTCALLBACK) status: %d", (IArg)status);
    if (status < 0) {
        Log_print1(Diags_USER7, "Processor_create_d> "
                "Ipc_control(STARTCALLBACK) failed: %d", status);
        goto fail;
    }


    /* get user-specified heapId */
    heapId = perCoreHeapId(proc->cpuId);
    createAndRegisterHeap = FALSE;

    if (heapId == Processor_INVALID) {
        /* runtime validation of user configuration */
        if (perCoreUserCreatedHeapFlag(proc->cpuId) == TRUE ||
                perCoreNumMsgs(proc->cpuId) != Processor_INVALID ||
                perCoreMsgSize(proc->cpuId) != Processor_INVALID ||
                perCoreSharedRegionId(proc->cpuId) != Processor_INVALID) {

            Log_print1(Diags_USER7, "[+7] Processor_create_d> "
                    "Invalid heap configuration for core %d: "
                    "attempting to set other Processor_CommDesc "
                    "elements while Processor_CommDesc.heapId is "
                    "undefined",
                    proc->cpuId);

            goto fail;
        }

        /* will return default heapId since user didn't specify */
        heapId = Processor_getHeapId(proc->cpuId);

        if (defaultHeapRefCount++ == 0) {
            createAndRegisterHeap = TRUE;

            /* tell code below to record heapH in defaultHeapH */
            defaultHeapH = (HeapBufMP_Handle)-1;
        }
    }
    else {
        if (perCoreUserCreatedHeapFlag(proc->cpuId) == FALSE) {
            createAndRegisterHeap = TRUE;
        }
    }

    if (createAndRegisterHeap) {
        /* create a heap for message queue usage */

        /* get either user-config'ed or module default */
        numMsgs = Processor_getNumMsgs(proc->cpuId);
        msgSize = Processor_getMsgSize(proc->cpuId);
        regionId = Processor_getSharedRegionId(proc->cpuId);

        HeapBufMP_Params_init(&heapP);
        heapP.numBlocks = numMsgs;
        heapP.blockSize = msgSize;
        heapP.sharedAddr = NULL;
        heapP.regionId = regionId;
        if (defaultHeapH == (HeapBufMP_Handle)-1) {
            sprintf(heapName, "CE-default");
        }
        else {
            sprintf(heapName, "CE<->Svr%d", proc->cpuId);
        }
        heapP.name = heapName;

        Log_print2(Diags_USER1, "[+2] Processor_create_d> "
                "calling HeapBufMP_create(): nblocks %d, blocksize 0x%x",
                heapP.numBlocks, heapP.blockSize);

        proc->heapH = HeapBufMP_create(&heapP);

        if (proc->heapH == NULL) {
            Log_print0(Diags_USER7, "[+7] Processor_create_d> "
                    "HeapBufMP_create failed");
            goto fail;
        }

        if (defaultHeapH == (HeapBufMP_Handle)-1) {
            /* we've just created the module default heap singleton */
            defaultHeapH = proc->heapH;
        }

        /* register this heap with MessageQ */
        Log_print2(Diags_USER1, "[+2] Processor_create_d> "
                "MessageQ_registerHeap(heapH: 0x%x, heapId: %d)",
                (IArg)(proc->heapH), (IArg)heapId);

        if (MessageQ_registerHeap((Ptr)(proc->heapH), heapId) !=
                    MessageQ_S_SUCCESS) {
        Log_print1(Diags_USER7, "[+7] Processor_create_d> "
                "MessageQ_registerHeap() failed for heapId %d", heapId);

        goto fail;
        }
    }
    else {
        /*
         *  createAndRegisterHeap == FASLE
         *  If using the default heap, need to set proc->heapH for use by
         *  procDelete().
         */
        if (heapId == Processor_defaultHeapId) {
            proc->heapH = defaultHeapH;
        }
    }

    proc->heapId = heapId;

    blockNum = 0;
    nCMEMBlocks = 0;
    status = CMEM_getNumBlocks(&nCMEMBlocks);
    if (status != 0) {
        Log_print1(Diags_USER2, "[+2] Processor_create_d> "
                "CMEM_getNumBlocks() failed, not registering: %d",
                status);
    }

    while (blockNum < nCMEMBlocks) {
        status = CMEM_getBlockAttrs(blockNum, &cmemBlockAttrs);
        if (status != 0) {
            Log_print2(Diags_USER7, "[+7] Processor_create_d> "
                    "CMEM_getBlockAttrs(%d) failed: %d",
                    blockNum, status);

            goto fail;
        }

        CMEMAddrInfo.addr[ProcMgr_AddrType_MasterPhys] =
                cmemBlockAttrs.phys_base;
        CMEMAddrInfo.addr[ProcMgr_AddrType_SlaveVirt] =
                cmemBlockAttrs.phys_base;
        CMEMAddrInfo.size = cmemBlockAttrs.size;
        CMEMAddrInfo.isCached = FALSE;

        Log_print3(Diags_USER1, "[+1] Processor_create_d> CMEM block "
                "#%d found, doing ProcMgr_map(0x%x, 0x%x)...", blockNum,
                (IArg)cmemBlockAttrs.phys_base,
                (IArg)cmemBlockAttrs.size);

        status = ProcMgr_map(proc->procMgrH, ProcMgr_SLAVEVIRT, &CMEMAddrInfo,
                ProcMgr_AddrType_MasterPhys);
        if (status < 0) {
            Log_print1(Diags_USER7, "[+7] Processor_create_d> "
                    "ProcMgr_map() failed: %d", status);
            goto fail;
        }

        if (CMEMAddrInfo.addr[ProcMgr_AddrType_SlaveVirt] !=
                cmemBlockAttrs.phys_base) {
            Log_print2(Diags_USER1, "[+2] Processor_create_d> "
                    "mapped CMEM slave virtual address 0x%x doesn't "
                    "match expected value 0x%x",
                    CMEMAddrInfo.addr[ProcMgr_AddrType_SlaveVirt],
                    cmemBlockAttrs.phys_base);
        }

        blockNum++;
    }

    if (Global_getenv("CE_DSPDEBUG") != NULL) {
        printf("Codec Engine system message (b/c CE_DSPDEBUG=1) : %s image "
               "loaded and started, press Enter to continue: ",
               proc->attrs.cpuId);
        getchar();
    }

    retVal = TRUE;
    goto procCreate_return;

    /* TODO:[4] should try those asyncErrorHandlers that link supports?
     * (MSGQ_SetErrorHandler)
     */

fail:
    Log_print3(Diags_USER7, "[+7] Processor_create_d> "
            "Loading and starting %s server '%s' FAILED, status=[0x%x]",
            (IArg)proc->attrs.cpuId, (IArg)proc->imageName, status);

    procDelete(proc);

    retVal = FALSE;

procCreate_return:

    Log_print1(Diags_USER2, "[+2] Processor_create_d> return (%d)",
            (IArg)retVal);

    return (retVal);
}
/*
 *  ======== mapByFile ========
 */
Bool mapByFile(ProcMgr_Handle handle, String mapFile, UInt16 procId,
        int mapFlag)
{
    FILE *f;
    char line[MAXLINE];
    MapStruct mapStruct;
    ProcMgr_AddrInfo addrInfo;
    Int status = 0;
    ProcMgr_ProcInfo *procInfo;
    Int procInfoSize;
    UInt32 maxMemoryRegions;
    int j;

    if (mapFile == NULL) {
        return (TRUE);
    }

    f = fopen(mapFile, "r");

    if (!f) {
        Log_print1(Diags_USER5, "[+5] mapByFile: Unable to open %s "
                "skipping....", (IArg)mapFile);

        /* regardless, we return 'success'.  Malformed lines are skipped */
        return (TRUE);
    }

    while (fgets(line, MAXLINE, f)) {
        if (!isMapLine(line, procId)) {
            Log_print1(Diags_USER5, "malformed line, skipping:\n   %s",
                    (IArg)line);
            continue;
        }

        /* Parse line, if not applicable, continue */
        if (!initFromLine(line, &mapStruct)) {
            continue;
        }

        /* applicable line */
        if (mapFlag) {
            /* map! */
            Log_print6(Diags_USER2,
                    "[+2] mapByFile: Mapping SV: 0x%x, SP: 0x%x, "
                    "MP: 0x%x,\n  size 0x%x, mask 0x%x, cached %d",
                    (IArg)mapStruct.slaveVirt, (IArg)mapStruct.slavePhys,
                    (IArg)mapStruct.masterPhys, (IArg)mapStruct.size,
                    (IArg)mapStruct.mapMask, (IArg)mapStruct.isCached);

            addrInfo.addr[MKV] = -1u;
            addrInfo.addr[ProcMgr_AddrType_MasterUsrVirt] = -1u;
            addrInfo.addr[MP] = mapStruct.masterPhys;
            addrInfo.addr[ProcMgr_AddrType_SlaveVirt] = mapStruct.slaveVirt;
            addrInfo.addr[ProcMgr_AddrType_SlavePhys] = -1u;
            addrInfo.size = mapStruct.size;
            addrInfo.isCached = mapStruct.isCached;

            status = ProcMgr_map(handle, mapStruct.mapMask, &addrInfo, MP);
            if (status < 0) {
                Log_print1(Diags_USER6, "[+6] mapByFile: ProcMgr_map "
                        "failed [0x%x]", (IArg)status);
            }
            else {
                Log_print0(Diags_USER2, "[+2] mapByFile: mapping succeeded");
            }
        }
        else {
            /* unmap! */
            Log_print6(Diags_USER2, "[+2]Unmapping SV: 0x%x, SP: 0x%x, "
                    "MP: 0x%x,\n  size 0x%x, mask 0x%x, cached %d",
                    (IArg)mapStruct.slaveVirt, (IArg)mapStruct.slavePhys,
                    (IArg)mapStruct.masterPhys, (IArg)mapStruct.size,
                    (IArg)mapStruct.mapMask, (IArg)mapStruct.isCached);

            addrInfo.addr[MKV] = -1u;

            /* if unmapping MasterKnlVirt, need to look it up */
            if (mapStruct.mapMask & ProcMgr_MASTERKNLVIRT) {

                maxMemoryRegions = ProcMgr_getMaxMemoryRegions(handle);
                procInfoSize = sizeof(ProcMgr_ProcInfo) +
                           (maxMemoryRegions * sizeof(ProcMgr_MappedMemEntry));
                procInfo = Memory_alloc(procInfoSize, NULL);

                status = ProcMgr_getProcInfo(handle, procInfo);
                if (status < 0) {
                    Log_print0(Diags_USER6, "[+6] Unable to find MKV entry");
                }
                else {
                    for (j = 0; j < procInfo->maxMemoryRegions; j++) {
                        if ((procInfo->memEntries[j].inUse) &&
                                (procInfo->memEntries[j].info.addr[MP]
                                        == mapStruct.masterPhys) &&
                                (procInfo->memEntries[j].info.size ==
                                        mapStruct.size) &&
                                (procInfo->memEntries[j].info.isCached
                                        == mapStruct.isCached)) {
                            /* found it */
                            addrInfo.addr[MKV] =
                                procInfo->memEntries[j].info.addr[MKV];
                            Log_print1(Diags_USER2,
                                    "[+2] Found MKV entry (0x%x)",
                                    (IArg)addrInfo.addr[MKV]);
                            break;
                        }
                    }
                }

                Memory_free(procInfo, procInfoSize, NULL);
            }

            addrInfo.addr[ProcMgr_AddrType_MasterUsrVirt] = -1u;
            addrInfo.addr[MP] = mapStruct.masterPhys;
            addrInfo.addr[ProcMgr_AddrType_SlaveVirt] =
                mapStruct.slaveVirt;
            addrInfo.addr[ProcMgr_AddrType_SlavePhys] = -1u;
            addrInfo.size = mapStruct.size;
            addrInfo.isCached = mapStruct.isCached;

            status = ProcMgr_unmap(handle, mapStruct.mapMask, &addrInfo, MP);
            if (status < 0) {
                Log_print1(Diags_USER6, "[+6] ProcMgr_unmap failed [0x%x]",
                        status);
            }
            else {
                Log_print0(Diags_USER2, "[+2] ProcMgr_unmap succeeded");
            }
        }
    }

    fclose(f);

    /* regardless, we return 'success'.  Malformed lines are skipped */
    return (TRUE);
}