.. _developer_notes_ipc_callback:

RPMessage IPC callback API
^^^^^^^^^^^^^^^^^^^^^^^^^^

In the 10.01.00 PSDK QNX release, a mechanism for registering for IPC callbacks was created.
These callbacks will trigger the registered callback function when a message corresponding to the
pre-specified endpoint is received. This section will describe in detail how to use this API.

Callback Signature
------------------

In a end use application, define a callback function with the following signature:

.. code:: C

    typedef void (*RPMessage_ProcCallback)(uint32_t srcEndPt, uint32_t srcProcId, uint32_t destEndPt, void* arg);

A short example of a qualifying callback:

.. code:: C

    void foo_cb(uint32_t srcEndPt, uint32_t srcProcId, uint32_t destEndPt, void* arg)
    {
        printf("Received a message from remote proc %u (endPt %u) on endPt %u\n", srcProcId, srcEndPt, destEndPt);
    }

Registering for Callbacks
-------------------------

With the callback function written, call :code:`RPMessage_setCallbackForProc()` and provide a pointer
for the callback function, along with a pointer to any arbitrary argument (in the form of a void 
pointer), and the thread priority at which to run the callback. The signature and description of
the registration function is shown below:

.. code:: C

    /**
    *  \brief Sets callback.
    *
    *  Sets the callback function and function arguments, to run for whenever
    *  an end point created by the calling process receives a message.
    *
    *  \param cb    [IN] \ref Ipc_NewMsgReceivedFxn Callback function.
    *  \param arg   [IN] Arguments of the callback function
    *  \param priority [IN] Scheduling priority to
    *
    *  \return     - #IPC_SOK: Calling process has successfully registered for a callback when
    *                          an associated end point receives a message.
    *              - #IPC_EFAIL: A general failure has occurred
    *
    */
    int32_t RPMessage_setCallbackForProc(RPMessage_ProcCallback cb, void* arg, int32_t priority);

A short example of registering for a callback with the example callback function would be:

.. code:: C

    int status = RPMessage_setCallbackForProc(&foo_cb, NULL, /* thread priority */ 10);

.. NOTE::
    
    Registering a process for IPC callbacks can be done at any point after the process has 
    called :code:`Ipc_init()`. After a callback has been registered, any future registered 
    endpoints (from :code:`RPMessage_create()`) corresponding to the process, that originally
    called :code:`RPMessage_setCallbackForProc()`, will automatically have callbacks enabled.

Functional Description of Callback API
--------------------------------------

This callback API is designed to make a call to the registered callback for any endpoint that is
associated with the calling process.

For example, say process "P1" registers endpoints "E1", "E2", and "E3", and process "P2" registers 
endpoints "E4", "E5", and "E6". Now say "P1" registers a callback :code:`foo_cb()` and "P2" 
registers a callback :code:`bar_cb()` . If any message is received on "E1" (or "E2"/"E3"), 
:code:`foo_cb()` will be called. Similarly if "E4" (or "E5"/"E6") receives a message then 
:code:`bar_cb()` would be called.

.. IMPORTANT::

    A callback does not automatically receive a message from the internal IPC queue. Once a callback
    is received, then a RPMessage_recv (or RPMessage_recvNb for nonblocking) should be made to the 
    corresponding RPMessage_Handle!

Long Example
------------

Included in PSDK QNX 10.01.00 is an update to ipc_test that adds a mode for testing using callbacks
instead of loops waiting of :code:`RPMessage_recv()`. The full length example is available in 
:code:`psdkqa/qnx/examples/ipc/ipc_test_qnx/src/ipc_testsetup.c`

.. code:: C

    void Ipc_echo_test_callback(uint32_t srcEndPt, uint32_t srcProcId, uint32_t destEndPt, void* arg)
    {
        int status, scanStatus;
        char buf[IPC_RPMESSAGE_MSG_BUFFER_SIZE];
        uint16_t recvLen;
        uint32_t remoteEndPt = ENDPT1;
        uint32_t remoteProcId;

        status = RPMessage_recvNb(gSenderThreadArgs[srcProcId].RPHandle, (Ptr)buf, &recvLen, &remoteEndPt, &remoteProcId);

        if (status != IPC_SOK)
        {
            printf("Error when calling RPMessage_recvNb...srcEndPt=%u, srcProcId=%u, destEndPt=%u, return status=%d (%s.%d)\n",
                srcEndPt, srcProcId, destEndPt, status, __FUNCTION__, __LINE__);
        }
        else
        {
            sem_post(gSenderThreadArgs[srcProcId].semHandle);
            trace_logi(IPC_TEST_TRACE_RECV_MARKER, remoteEndPt, remoteProcId);
            timespec_get(&lastCallbackUTC, TIME_UTC);

            scanStatus = validatePongMessage((char *)buf, recvLen, gSenderThreadArgs[srcProcId].cntPong);
            switch (scanStatus)
            {
                case 0:
                    if (gVerbose)
                    {
                        printf("Remote%u: Successfully received \""PONG_MSG_FORMAT"\" from %s endPt %d, status=%d\n",
                            srcProcId, (int)gSenderThreadArgs[srcProcId].cntPong, Ipc_mpGetName(srcProcId), srcEndPt, status);
                    }

                    gSenderThreadArgs[srcProcId].cntPong++;
                    break;

                case IPC_EFAIL:
                    printf("Remote%u: Failed to parse message \"%s\" for a pong index. status=%d\n", srcProcId, buf, status);
                    break;

                default:
                    printf("Remote%u: Received \""PONG_MSG_FORMAT"\" but expected \""PONG_MSG_FORMAT"\"",
                        srcProcId, scanStatus, (int)gSenderThreadArgs[srcProcId].cntPong);
                    break;
            }
        }
    }

    // ...

    int32_t Ipc_callback_echo_test(void)
    {

        // ...
        
        /* Register for callbacks */
        printf("%s: Setting callback function for process\n", __FUNCTION__);
        status = RPMessage_setCallbackForProc(&Ipc_echo_test_callback, (void*)0xDEADBEEF, 63);
        if (IPC_SOK != status)
        {
            fprintf(stderr, "Error setting callback function...status=%d (%s.%d)\n", status, __FUNCTION__, __LINE__);
        }

        // ...
    }

    // ...