/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: import/chips/p9/common/pmlib/occlib/ipc_msgq.c $              */
/*                                                                        */
/* OpenPOWER HCODE Project                                                */
/*                                                                        */
/* COPYRIGHT 2015,2017                                                    */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */

/// \file ipc_msgq.c
/// \brief Implementation of IPC (InterProcessor Communication) routines that
///        involve using message queues (primarily in a thread context).

#include "ipc_api.h"

///////////////////////////////////////////////////////////////////////////////
// Handles msgq messages (commands and responses) by placing them on a message
// queue and then posting the message queue semaphore.
// This function is for internal use only!
void ipc_msgq_handler(ipc_msg_t* msg, void* arg)
{
    ipc_msgq_t*  msgq = (ipc_msgq_t*)arg;

    //NOTE: this is hard coded to 0 on PPE
    if(KERN_CONTEXT_CRITICAL_INTERRUPT())
    {
        //NOTE: this is a no-op on PPE
        IPC_DEFER_TO_NONCRITICAL(msg);
    }
    else
    {
        KERN_DEQUE_PUSH_BACK(&msgq->msg_head, &msg->node);
        KERN_SEMAPHORE_POST(&msgq->msg_sem);
    }
}


///////////////////////////////////////////////////////////////////////////////
/// Wait (with timeout) for an IPC message on an IPC message queue.
///
int ipc_msgq_recv(ipc_msg_t** msg, ipc_msgq_t* msgq, KERN_INTERVAL timeout)
{
    int                     rc;
    ipc_msg_t*               popped_msg = 0;
    KERN_MACHINE_CONTEXT    ctx;

    // First check for pending messages already on the queue.
    KERN_CRITICAL_SECTION_ENTER(KERN_CRITICAL, &ctx);
    popped_msg = (ipc_msg_t*)KERN_DEQUE_POP_FRONT(&msgq->msg_head);

    if(popped_msg)
    {
        KERN_CRITICAL_SECTION_EXIT(&ctx);
        rc = IPC_RC_SUCCESS;
    }
    else  // no message - wait for one
    {

        rc = KERN_SEMAPHORE_PEND(&msgq->msg_sem, timeout);
        KERN_CRITICAL_SECTION_EXIT(&ctx);

        if(rc)
        {
            if(rc == -KERN_SEMAPHORE_PEND_TIMED_OUT ||
               rc == -KERN_SEMAPHORE_PEND_NO_WAIT)
            {
                rc = IPC_RC_TIMEOUT;
            }
        }
        else
        {
            //The queue is also modified in the IPC interrupt context so
            //we need to make sure interrupts are disabled while we modify it.
            KERN_CRITICAL_SECTION_ENTER(KERN_CRITICAL, &ctx);
            popped_msg = (ipc_msg_t*)KERN_DEQUE_POP_FRONT(&msgq->msg_head);
            KERN_CRITICAL_SECTION_EXIT(&ctx);

            if(popped_msg)
            {
                rc = IPC_RC_SUCCESS;
            }
            else
            {
                rc = IPC_RC_NO_MSG;
            }
        }
    }

    *msg = popped_msg;
    return rc;
}

///////////////////////////////////////////////////////////////////////////////
/// Associate a message queue with a function ID (commands recieved with the
/// specified function id will be placed on the message queue).
///
int ipc_register_msgq(uint32_t func_id, ipc_msgq_t* msgq)
{
    return ipc_set_handler(func_id, ipc_msgq_handler, msgq);
}
