Added missing package dependencies.
[ipc/ipcdev.git] / packages / ti / ipc / rpmsg / RPMessage.c
1 /*
2  * Copyright (c) 2011-2013, Texas Instruments Incorporated
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * *  Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * *  Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * *  Neither the name of Texas Instruments Incorporated nor the names of
17  *    its contributors may be used to endorse or promote products derived
18  *    from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 /** ============================================================================
33  *  @file       RPMessage.c
34  *
35  *  @brief      A simple copy-based MessageQ, to work with Linux virtio_rp_msg.
36  *
37  *  Notes:
38  *  - The logic in the functions for sending (_put()) and receiving _swiFxn()
39  *    depend on the role (host or slave) the processor is playing in the
40  *    asymmetric virtio I/O.
41  *  - The host always adds *available* buffers to send/receive, while the slave
42  *    always adds *used* buffers to send/receive.
43  *  - The logic is summarized below:
44  *
45  *    Host:
46  *    - Prime vq_host with avail bufs, and kick vq_host so slave can send.
47  *    - To send a buffer to the slave processor:
48  *          allocate a tx buffer, or get_used_buf(vq_slave);
49  *               >> copy data into buf <<
50  *          add_avail_buf(vq_slave);
51  *          kick(vq_slave);
52  *    - To receive buffer from slave processor:
53  *          get_used_buf(vq_host);
54  *              >> empty data from buf <<
55  *          add_avail_buf(vq_host);
56  *          kick(vq_host);
57  *
58  *    Slave:
59  *    - To receive buffer from the host:
60  *          get_avail_buf(vq_slave);
61  *              >> empty data from buf <<
62  *          add_used_buf(vq_slave);
63  *          kick(vq_slave);
64  *    - To send buffer to the host:
65  *          get_avail_buf(vq_host);
66  *              >> copy data into buf <<
67  *          add_used_buf(vq_host);
68  *          kick(vq_host);
69  *
70  *  ============================================================================
71  */
73 /* this define must precede inclusion of any xdc header file */
74 #define Registry_CURDESC ti_ipc_rpmsg_RPMessage__Desc
75 #define MODULE_NAME "ti.ipc.rpmsg.RPMessage"
77 #include <xdc/std.h>
78 #include <string.h>
80 #include <xdc/runtime/System.h>
81 #include <xdc/runtime/Assert.h>
82 #include <xdc/runtime/Memory.h>
83 #include <xdc/runtime/Registry.h>
84 #include <xdc/runtime/Log.h>
85 #include <xdc/runtime/Diags.h>
87 #include <ti/sysbios/BIOS.h>
88 #include <ti/sysbios/knl/Swi.h>
89 #include <ti/sysbios/knl/Semaphore.h>
90 #include <ti/sysbios/heaps/HeapBuf.h>
91 #include <ti/sysbios/gates/GateAll.h>
93 #include <ti/sdo/utils/List.h>
94 #include <ti/ipc/MultiProc.h>
96 #include <ti/ipc/rpmsg/RPMessage.h>
98 #include "_VirtQueue.h"
100 /* TBD: VirtQueue.h needs to somehow get factored out of family directory .*/
101 #if defined(OMAPL138)
102 #include <ti/ipc/family/omapl138/VirtQueue.h>
103 #elif defined(TCI6614)
104 #include <ti/ipc/family/tci6614/VirtQueue.h>
105 #elif defined(TCI6638)
106 #include <ti/ipc/family/tci6638/VirtQueue.h>
107 #elif defined(OMAP5)
108 #include <ti/ipc/family/omap54xx/VirtQueue.h>
109 #else
110 #error unknown processor!
111 #endif
113 /* =============================================================================
114  * Structures & Enums
115  * =============================================================================
116  */
118 /* Various arbitrary limits: */
119 #define MAXMESSAGEQOBJECTS     256
120 #define MAXMESSAGEBUFFERS      512
121 #define MSGBUFFERSIZE          512   // Max payload + sizeof(ListElem)
122 #define MAXHEAPSIZE            (MAXMESSAGEBUFFERS * MSGBUFFERSIZE)
123 #define HEAPALIGNMENT          8
125 /* The RPMessage Object */
126 typedef struct RPMessage_Object {
127     UInt32           queueId;      /* Unique id (procId | queueIndex)       */
128     Semaphore_Handle semHandle;    /* I/O Completion                        */
129     RPMessage_callback cb;      /* RPMessage Callback */
130     UArg             arg;          /* Callback argument */
131     List_Handle      queue;        /* Queue of pending messages             */
132     Bool             unblocked;    /* Use with signal to unblock _receive() */
133 } RPMessage_Object;
135 /* Module_State */
136 typedef struct RPMessage_Module {
137     /* Instance gate: */
138     GateAll_Handle gateH;
139     /* Array of messageQObjects in the system: */
140     struct RPMessage_Object  *msgqObjects[MAXMESSAGEQOBJECTS];
141     /* Heap from which to allocate free messages for copying: */
142     HeapBuf_Handle              heap;
143 } RPMessage_Module;
145 /* Message Header: Must match mp_msg_hdr in virtio_rp_msg.h on Linux side. */
146 typedef struct RPMessage_MsgHeader {
147     Bits32 srcAddr;                 /* source endpoint addr               */
148     Bits32 dstAddr;                 /* destination endpoint addr          */
149     Bits32 reserved;                /* reserved                           */
150     Bits16 dataLen;                 /* data length                        */
151     Bits16 flags;                   /* bitmask of different flags         */
152     UInt8  payload[];               /* Data payload                       */
153 } RPMessage_MsgHeader;
155 typedef RPMessage_MsgHeader *RPMessage_Msg;
157 /* Element to hold payload copied onto receiver's queue.                  */
158 typedef struct Queue_elem {
159     List_Elem    elem;              /* Allow list linking.                */
160     UInt         len;               /* Length of data                     */
161     UInt32       src;               /* Src address/endpt of the msg       */
162     Char         data[];            /* payload begins here                */
163 } Queue_elem;
165 /* Combine transport related objects into a struct for future migration: */
166 typedef struct RPMessage_Transport  {
167     Swi_Handle       swiHandle;
168     VirtQueue_Handle virtQueue_toHost;
169     VirtQueue_Handle virtQueue_fromHost;
170     Semaphore_Handle semHandle_toHost;
171 } RPMessage_Transport;
174 /* module diags mask */
175 Registry_Desc Registry_CURDESC;
177 static RPMessage_Module      module;
178 static RPMessage_Transport   transport;
180 /* We create a fixed size heap over this memory for copying received msgs */
181 #pragma DATA_ALIGN (recv_buffers, HEAPALIGNMENT)
182 static UInt8 recv_buffers[MAXHEAPSIZE];
184 /* Module ref count: */
185 static Int curInit = 0;
187 /*
188  *  ======== RPMessage_swiFxn ========
189  */
190 #define FXNN "RPMessage_swiFxn"
191 static Void RPMessage_swiFxn(UArg arg0, UArg arg1)
193     Int16             token;
194     RPMessage_Msg  msg;
195     UInt16            dstProc = MultiProc_self();
196     Bool              usedBufAdded = FALSE;
197     int len;
199     Log_print0(Diags_ENTRY, "--> "FXNN);
201     /* Process all available buffers: */
202     while ((token = VirtQueue_getAvailBuf(transport.virtQueue_fromHost,
203                                          (Void **)&msg, &len)) >= 0) {
205         Log_print3(Diags_INFO, FXNN": \n\tReceived msg: from: 0x%x, "
206                    "to: 0x%x, dataLen: %d",
207                   (IArg)msg->srcAddr, (IArg)msg->dstAddr, (IArg)msg->dataLen);
209         /* Pass to destination queue (on this proc), or callback: */
210         RPMessage_send(dstProc, msg->dstAddr, msg->srcAddr,
211                          (Ptr)msg->payload, msg->dataLen);
213         VirtQueue_addUsedBuf(transport.virtQueue_fromHost, token,
214                                                             RPMSG_BUF_SIZE);
215         usedBufAdded = TRUE;
216     }
218     if (usedBufAdded)  {
219        /* Tell host we've processed the buffers: */
220        VirtQueue_kick(transport.virtQueue_fromHost);
221     }
222     Log_print0(Diags_EXIT, "<-- "FXNN);
224 #undef FXNN
227 #define FXNN "callback_availBufReady"
228 static Void callback_availBufReady(VirtQueue_Handle vq)
231     if (vq == transport.virtQueue_fromHost)  {
232        /* Post a SWI to process all incoming messages */
233         Log_print0(Diags_INFO, FXNN": virtQueue_fromHost kicked");
234         Swi_post(transport.swiHandle);
235     }
236     else if (vq == transport.virtQueue_toHost) {
237        /* Note: We normally post nothing for transport.virtQueue_toHost,
238         * unless we were starved for buffers, and we turned on notifications.
239         */
240         Semaphore_post(transport.semHandle_toHost);
241         Log_print0(Diags_INFO, FXNN": virtQueue_toHost kicked");
242     }
244 #undef FXNN
246 /* =============================================================================
247  *  RPMessage Functions:
248  * =============================================================================
249  */
251 /*
252  *  ======== MessasgeQCopy_init ========
253  *
254  *
255  */
256 #define FXNN "RPMessage_init"
257 Void RPMessage_init(UInt16 remoteProcId)
259     GateAll_Params gatePrms;
260     HeapBuf_Params prms;
261     int     i;
262     Registry_Result result;
263     Bool    isHost;
264     VirtQueue_Params vqParams;
266     if (curInit++) {
267         Log_print1(Diags_ENTRY, "--> "FXNN": (remoteProcId=%d)",
268                     (IArg)remoteProcId);
269         goto exit;  /* module already initialized */
270     }
272     /* register with xdc.runtime to get a diags mask */
273     result = Registry_addModule(&Registry_CURDESC, MODULE_NAME);
274     Assert_isTrue(result == Registry_SUCCESS, (Assert_Id)NULL);
276     /* Log should be after the Registry_CURDESC is initialized */
277     Log_print1(Diags_ENTRY, "--> "FXNN": (remoteProcId=%d)",
278                 (IArg)remoteProcId);
280     /* Gate to protect module object and lists: */
281     GateAll_Params_init(&gatePrms);
282     module.gateH = GateAll_create(&gatePrms, NULL);
284     /* Initialize Module State: */
285     for (i = 0; i < MAXMESSAGEQOBJECTS; i++) {
286        module.msgqObjects[i] = NULL;
287     }
289     HeapBuf_Params_init(&prms);
290     prms.blockSize    = MSGBUFFERSIZE;
291     prms.numBlocks    = MAXMESSAGEBUFFERS;
292     prms.buf          = recv_buffers;
293     prms.bufSize      = MAXHEAPSIZE;
294     prms.align        = HEAPALIGNMENT;
295     module.heap       = HeapBuf_create(&prms, NULL);
296     if (module.heap == 0) {
297        System_abort("RPMessage_init: HeapBuf_create returned 0\n");
298     }
299     transport.semHandle_toHost = Semaphore_create(0, NULL, NULL);
301     isHost = (MultiProc_self() == MultiProc_getId("HOST"));
303     /* Initialize Transport related objects: */
305     VirtQueue_Params_init(&vqParams);
306     if (isHost)  {
307       /* We don't handle this case currently! Host would need to prime vq. */
308       Assert_isTrue(FALSE, NULL);
309     }
310     else {
311       vqParams.callback = callback_availBufReady;
312     }
314     /*
315      * Create a pair VirtQueues (one for sending, one for receiving).
316      * Note: First one gets an even, second gets odd vq ID.
317      */
318     vqParams.vqId = ID_SELF_TO_A9;
319     transport.virtQueue_toHost   = (Ptr)VirtQueue_create(remoteProcId,
320                                                     &vqParams, NULL);
321     vqParams.vqId = ID_A9_TO_SELF;
322     transport.virtQueue_fromHost = (Ptr)VirtQueue_create(remoteProcId,
323                                                     &vqParams, NULL);
325     /* Plug Vring Interrupts, and wait for host ready to recv kick: */
326     VirtQueue_startup(remoteProcId, isHost);
328     /* construct the Swi to process incoming messages: */
329     transport.swiHandle = Swi_create(RPMessage_swiFxn, NULL, NULL);
331 exit:
332     Log_print0(Diags_EXIT, "<-- "FXNN);
334 #undef FXNN
336 /*
337  *  ======== MessasgeQCopy_finalize ========
338  */
339 #define FXNN "RPMessage_finalize"
340 Void RPMessage_finalize()
342     Log_print0(Diags_ENTRY, "--> "FXNN);
344     if (!curInit || --curInit) {
345          goto exit; /* module still in use, or uninitialized */
346     }
348     /* Tear down Module */
349     HeapBuf_delete(&(module.heap));
351     Swi_delete(&(transport.swiHandle));
353     GateAll_delete(&module.gateH);
355 exit:
356     Log_print0(Diags_EXIT, "<-- "FXNN);
358 #undef FXNN
360 /*
361  *  ======== RPMessage_create ========
362  */
363 #define FXNN "RPMessage_create"
364 RPMessage_Handle RPMessage_create(UInt32 reserved,
365                                         RPMessage_callback cb,
366                                         UArg arg,
367                                         UInt32 * endpoint)
369     RPMessage_Object    *obj = NULL;
370     Bool                   found = FALSE;
371     Int                    i;
372     UInt16                 queueIndex = 0;
373     IArg key;
375     Log_print4(Diags_ENTRY, "--> "FXNN": "
376                 "(reserved=%d, cb=0x%x, arg=0x%x, endpoint=0x%x)",
377                 (IArg)reserved, (IArg)cb, (IArg)arg, (IArg)endpoint);
379     Assert_isTrue((curInit > 0) , NULL);
381     key = GateAll_enter(module.gateH);
383     if (reserved == RPMessage_ASSIGN_ANY)  {
384        /* Search the array for a free slot above reserved: */
385        for (i = RPMessage_MAX_RESERVED_ENDPOINT + 1;
386            (i < MAXMESSAGEQOBJECTS) && (found == FALSE) ; i++) {
387            if (module.msgqObjects[i] == NULL) {
388             queueIndex = i;
389             found = TRUE;
390             break;
391            }
392        }
393     }
394     else if ((queueIndex = reserved) <= RPMessage_MAX_RESERVED_ENDPOINT) {
395        if (module.msgqObjects[queueIndex] == NULL) {
396            found = TRUE;
397        }
398     }
400     if (found)  {
401        obj = Memory_alloc(NULL, sizeof(RPMessage_Object), 0, NULL);
402        if (obj != NULL) {
403            if (cb) {
404                /* Store callback and it's arg instead of semaphore: */
405                obj->cb = cb;
406                obj->arg= arg;
407            }
408            else {
409                obj->cb = NULL;
411                /* Allocate a semaphore to signal when messages received: */
412                obj->semHandle = Semaphore_create(0, NULL, NULL);
414                /* Create our queue of to be received messages: */
415                obj->queue = List_create(NULL, NULL);
416            }
418            /* Store our endpoint, and object: */
419            obj->queueId = queueIndex;
420            module.msgqObjects[queueIndex] = obj;
422            /* See RPMessage_unblock() */
423            obj->unblocked = FALSE;
425            *endpoint    = queueIndex;
426            Log_print1(Diags_LIFECYCLE, FXNN": endPt created: %d",
427                         (IArg)queueIndex);
428        }
429     }
431     GateAll_leave(module.gateH, key);
433     Log_print1(Diags_EXIT, "<-- "FXNN": 0x%x", (IArg)obj);
434     return (obj);
436 #undef FXNN
438 /*
439  *  ======== RPMessage_delete ========
440  */
441 #define FXNN "RPMessage_delete"
442 Int RPMessage_delete(RPMessage_Handle *handlePtr)
444     Int                    status = RPMessage_S_SUCCESS;
445     RPMessage_Object    *obj;
446     Queue_elem             *payload;
447     IArg                   key;
449     Log_print1(Diags_ENTRY, "--> "FXNN": (handlePtr=0x%x)", (IArg)handlePtr);
451     Assert_isTrue((curInit > 0) , NULL);
453     if (handlePtr && (obj = (RPMessage_Object *)(*handlePtr)))  {
455        if (obj->cb) {
456            obj->cb = NULL;
457            obj->arg= NULL;
458        }
459        else {
460            Semaphore_delete(&(obj->semHandle));
462            /* Free/discard all queued message buffers: */
463            while ((payload = (Queue_elem *)List_get(obj->queue)) != NULL) {
464                HeapBuf_free(module.heap, (Ptr)payload, MSGBUFFERSIZE);
465            }
467            List_delete(&(obj->queue));
468        }
470        /* Null out our slot: */
471        key = GateAll_enter(module.gateH);
472        module.msgqObjects[obj->queueId] = NULL;
473        GateAll_leave(module.gateH, key);
475        Log_print1(Diags_LIFECYCLE, FXNN": endPt deleted: %d",
476                         (IArg)obj->queueId);
478        /* Now free the obj */
479        Memory_free(NULL, obj, sizeof(RPMessage_Object));
481        *handlePtr = NULL;
482     }
484     Log_print1(Diags_EXIT, "<-- "FXNN": %d", (IArg)status);
485     return(status);
487 #undef FXNN
489 /*
490  *  ======== RPMessage_recv ========
491  */
492 #define FXNN "RPMessage_recv"
493 Int RPMessage_recv(RPMessage_Handle handle, Ptr data, UInt16 *len,
494                       UInt32 *rplyEndpt, UInt timeout)
496     Int                 status = RPMessage_S_SUCCESS;
497     RPMessage_Object *obj = (RPMessage_Object *)handle;
498     Bool                semStatus;
499     Queue_elem          *payload;
501     Log_print5(Diags_ENTRY, "--> "FXNN": (handle=0x%x, data=0x%x, len=0x%x,"
502                "rplyEndpt=0x%x, timeout=%d)", (IArg)handle, (IArg)data,
503                (IArg)len, (IArg)rplyEndpt, (IArg)timeout);
505     Assert_isTrue((curInit > 0) , NULL);
506     /* A callback was set: client should not be calling this fxn! */
507     Assert_isTrue((!obj->cb), NULL);
509     /* Check vring for pending messages before we block: */
510     Swi_post(transport.swiHandle);
512     /*  Block until notified. */
513     semStatus = Semaphore_pend(obj->semHandle, timeout);
515     if (semStatus == FALSE)  {
516        status = RPMessage_E_TIMEOUT;
517        Log_print0(Diags_STATUS, FXNN": Sem pend timeout!");
518     }
519     else if (obj->unblocked) {
520        status = RPMessage_E_UNBLOCKED;
521     }
522     else  {
523        payload = (Queue_elem *)List_get(obj->queue);
524        Assert_isTrue((!payload), NULL);
525     }
527     if (status == RPMessage_S_SUCCESS)  {
528        /* Now, copy payload to client and free our internal msg */
529        memcpy(data, payload->data, payload->len);
530        *len = payload->len;
531        *rplyEndpt = payload->src;
533        HeapBuf_free(module.heap, (Ptr)payload,
534                     (payload->len + sizeof(Queue_elem)));
535     }
537     Log_print1(Diags_EXIT, "<-- "FXNN": %d", (IArg)status);
538     return (status);
540 #undef FXNN
542 /*
543  *  ======== RPMessage_send ========
544  */
545 #define FXNN "RPMessage_send"
546 Int RPMessage_send(UInt16 dstProc,
547                       UInt32 dstEndpt,
548                       UInt32 srcEndpt,
549                       Ptr    data,
550                       UInt16 len)
552     Int               status = RPMessage_S_SUCCESS;
553     RPMessage_Object   *obj;
554     Int16             token = 0;
555     RPMessage_Msg  msg;
556     Queue_elem        *payload;
557     UInt              size;
558     IArg              key;
559     int length;
561     Log_print5(Diags_ENTRY, "--> "FXNN": (dstProc=%d, dstEndpt=%d, "
562                "srcEndpt=%d, data=0x%x, len=%d", (IArg)dstProc, (IArg)dstEndpt,
563                (IArg)srcEndpt, (IArg)data, (IArg)len);
565     Assert_isTrue((curInit > 0) , NULL);
567     if (dstProc != MultiProc_self()) {
568         /* Send to remote processor: */
569         do {
570             token = VirtQueue_getAvailBuf(transport.virtQueue_toHost,
571                     (Void **)&msg, &length);
572         } while (token < 0 && Semaphore_pend(transport.semHandle_toHost,
573                                              BIOS_WAIT_FOREVER));
574         if (token >= 0) {
575             /* Copy the payload and set message header: */
576             memcpy(msg->payload, data, len);
577             msg->dataLen = len;
578             msg->dstAddr = dstEndpt;
579             msg->srcAddr = srcEndpt;
580             msg->flags = 0;
581             msg->reserved = 0;
583             VirtQueue_addUsedBuf(transport.virtQueue_toHost, token,
584                                                             RPMSG_BUF_SIZE);
585             VirtQueue_kick(transport.virtQueue_toHost);
586         }
587         else {
588             status = RPMessage_E_FAIL;
589             Log_print0(Diags_STATUS, FXNN": getAvailBuf failed!");
590         }
591     }
592     else {
593         /* Put on a Message queue on this processor: */
595         /* Protect from RPMessage_delete */
596         key = GateAll_enter(module.gateH);
597         obj = module.msgqObjects[dstEndpt];
598         GateAll_leave(module.gateH, key);
600         if (obj == NULL) {
601             Log_print1(Diags_STATUS, FXNN": no object for endpoint: %d",
602                    (IArg)dstEndpt);
603             status = RPMessage_E_NOENDPT;
604             return status;
605         }
607         /* If callback registered, call it: */
608         if (obj->cb) {
609             Log_print2(Diags_INFO, FXNN": calling callback with data len: "
610                             "%d, from: %d\n", len, srcEndpt);
611             obj->cb(obj, obj->arg, data, len, srcEndpt);
612         }
613         else {
614             /* else, put on a Message queue on this processor: */
615             /* Allocate a buffer to copy the payload: */
616             size = len + sizeof(Queue_elem);
618             /* HeapBuf_alloc() is non-blocking, so needs protection: */
619             key = GateAll_enter(module.gateH);
620             payload = (Queue_elem *)HeapBuf_alloc(module.heap, size, 0, NULL);
621             GateAll_leave(module.gateH, key);
623             if (payload != NULL)  {
624                 memcpy(payload->data, data, len);
625                 payload->len = len;
626                 payload->src = srcEndpt;
628                 /* Put on the endpoint's queue and signal: */
629                 List_put(obj->queue, (List_Elem *)payload);
630                 Semaphore_post(obj->semHandle);
631             }
632             else {
633                 status = RPMessage_E_MEMORY;
634                 Log_print0(Diags_STATUS, FXNN": HeapBuf_alloc failed!");
635             }
636         }
637     }
639     Log_print1(Diags_EXIT, "<-- "FXNN": %d", (IArg)status);
640     return (status);
642 #undef FXNN
644 /*
645  *  ======== RPMessage_unblock ========
646  */
647 #define FXNN "RPMessage_unblock"
648 Void RPMessage_unblock(RPMessage_Handle handle)
650     RPMessage_Object *obj = (RPMessage_Object *)handle;
652     Log_print1(Diags_ENTRY, "--> "FXNN": (handle=0x%x)", (IArg)handle);
654     Assert_isTrue((!obj->cb), NULL);
655     /* Set instance to 'unblocked' state, and post */
656     obj->unblocked = TRUE;
657     Semaphore_post(obj->semHandle);
658     Log_print0(Diags_EXIT, "<-- "FXNN);
660 #undef FXNN