11.9. 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.
11.9.1. Callback Signature¶
In a end use application, define a callback function with the following signature:
typedef void (*RPMessage_ProcCallback)(uint32_t srcEndPt, uint32_t srcProcId, uint32_t destEndPt, void* arg);
A short example of a qualifying callback:
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);
}
11.9.2. Registering for Callbacks¶
With the callback function written, call 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:
/**
* \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:
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 Ipc_init()
. After a callback has been registered, any future registered
endpoints (from RPMessage_create()
) corresponding to the process, that originally
called RPMessage_setCallbackForProc()
, will automatically have callbacks enabled.
11.9.3. 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 foo_cb()
and “P2”
registers a callback bar_cb()
. If any message is received on “E1” (or “E2”/”E3”),
foo_cb()
will be called. Similarly if “E4” (or “E5”/”E6”) receives a message then
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!
11.9.4. 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 RPMessage_recv()
. The full length example is available in
psdkqa/qnx/examples/ipc/ipc_test_qnx/src/ipc_testsetup.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__);
}
// ...
}
// ...