1 /*
2 * Copyright (c) 2011-2014, 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/GateHwi.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 #elif defined(VAYU)
110 #include <ti/ipc/family/vayu/VirtQueue.h>
111 #else
112 #error unknown processor!
113 #endif
115 /* =============================================================================
116 * Structures & Enums
117 * =============================================================================
118 */
120 /* Various arbitrary limits: */
121 #define MAXMESSAGEQOBJECTS 256
122 #define MAXMESSAGEBUFFERS 512
123 #define MSGBUFFERSIZE 512 /* Max payload + sizeof(ListElem) */
124 #define MAXHEAPSIZE (MAXMESSAGEBUFFERS * MSGBUFFERSIZE)
125 #define HEAPALIGNMENT 8
127 /* The RPMessage Object */
128 typedef struct RPMessage_Object {
129 UInt32 queueId; /* Unique id (procId | queueIndex) */
130 Semaphore_Handle semHandle; /* I/O Completion */
131 RPMessage_callback cb; /* RPMessage Callback */
132 UArg arg; /* Callback argument */
133 List_Handle queue; /* Queue of pending messages */
134 Bool unblocked; /* Use with signal to unblock _receive() */
135 } RPMessage_Object;
137 /* Module_State */
138 typedef struct RPMessage_Module {
139 /* Instance gate: */
140 GateHwi_Handle gateH;
141 /* Array of messageQObjects in the system: */
142 struct RPMessage_Object *msgqObjects[MAXMESSAGEQOBJECTS];
143 /* Heap from which to allocate free messages for copying: */
144 HeapBuf_Handle heap;
145 } RPMessage_Module;
147 /* Message Header: Must match mp_msg_hdr in virtio_rp_msg.h on Linux side. */
148 typedef struct RPMessage_MsgHeader {
149 Bits32 srcAddr; /* source endpoint addr */
150 Bits32 dstAddr; /* destination endpoint addr */
151 Bits32 reserved; /* reserved */
152 Bits16 dataLen; /* data length */
153 Bits16 flags; /* bitmask of different flags */
154 UInt8 payload[]; /* Data payload */
155 } RPMessage_MsgHeader;
157 typedef RPMessage_MsgHeader *RPMessage_Msg;
159 /* Element to hold payload copied onto receiver's queue. */
160 typedef struct Queue_elem {
161 List_Elem elem; /* Allow list linking. */
162 UInt len; /* Length of data */
163 UInt32 src; /* Src address/endpt of the msg */
164 Char data[]; /* payload begins here */
165 } Queue_elem;
167 /* Combine transport related objects into a struct for future migration: */
168 typedef struct RPMessage_Transport {
169 Swi_Handle swiHandle;
170 VirtQueue_Handle virtQueue_toHost;
171 VirtQueue_Handle virtQueue_fromHost;
172 Semaphore_Handle semHandle_toHost;
173 } RPMessage_Transport;
176 /* module diags mask */
177 Registry_Desc Registry_CURDESC;
179 static RPMessage_Module module;
180 static RPMessage_Transport transport;
182 /* We create a fixed size heap over this memory for copying received msgs */
183 #pragma DATA_ALIGN (recv_buffers, HEAPALIGNMENT)
184 static UInt8 recv_buffers[MAXHEAPSIZE];
186 /* Module ref count: */
187 static Int curInit = 0;
189 /*
190 * ======== RPMessage_swiFxn ========
191 */
192 #define FXNN "RPMessage_swiFxn"
193 static Void RPMessage_swiFxn(UArg arg0, UArg arg1)
194 {
195 Int16 token;
196 RPMessage_Msg msg;
197 UInt16 dstProc = MultiProc_self();
198 Bool usedBufAdded = FALSE;
199 int len;
201 Log_print0(Diags_ENTRY, "--> "FXNN);
203 /* Process all available buffers: */
204 while ((token = VirtQueue_getAvailBuf(transport.virtQueue_fromHost,
205 (Void **)&msg, &len)) >= 0) {
207 Log_print3(Diags_INFO, FXNN": Received msg from: 0x%x, "
208 "to: 0x%x, dataLen: %d",
209 (IArg)msg->srcAddr, (IArg)msg->dstAddr, (IArg)msg->dataLen);
211 /* Pass to destination queue (on this proc), or callback: */
212 RPMessage_send(dstProc, msg->dstAddr, msg->srcAddr,
213 (Ptr)msg->payload, msg->dataLen);
215 VirtQueue_addUsedBuf(transport.virtQueue_fromHost, token,
216 RPMSG_BUF_SIZE);
217 usedBufAdded = TRUE;
218 }
220 if (usedBufAdded) {
221 /* Tell host we've processed the buffers: */
222 VirtQueue_kick(transport.virtQueue_fromHost);
223 }
224 Log_print0(Diags_EXIT, "<-- "FXNN);
225 }
226 #undef FXNN
229 #define FXNN "callback_availBufReady"
230 static Void callback_availBufReady(VirtQueue_Handle vq)
231 {
233 if (vq == transport.virtQueue_fromHost) {
234 /* Post a SWI to process all incoming messages */
235 Log_print0(Diags_INFO, FXNN": virtQueue_fromHost kicked");
236 Swi_post(transport.swiHandle);
237 }
238 else if (vq == transport.virtQueue_toHost) {
239 /* Note: We normally post nothing for transport.virtQueue_toHost,
240 * unless we were starved for buffers, and we turned on notifications.
241 */
242 Semaphore_post(transport.semHandle_toHost);
243 Log_print0(Diags_INFO, FXNN": virtQueue_toHost kicked");
244 }
245 }
246 #undef FXNN
248 /* =============================================================================
249 * RPMessage Functions:
250 * =============================================================================
251 */
253 /*
254 * ======== MessasgeQCopy_init ========
255 *
256 *
257 */
258 #define FXNN "RPMessage_init"
259 Void RPMessage_init(UInt16 remoteProcId)
260 {
261 GateHwi_Params gatePrms;
262 HeapBuf_Params prms;
263 Semaphore_Params semParams;
264 int i;
265 Registry_Result result;
266 Bool isHost;
267 VirtQueue_Params vqParams;
269 if (curInit++) {
270 Log_print1(Diags_ENTRY, "--> "FXNN": (remoteProcId=%d)",
271 (IArg)remoteProcId);
272 goto exit; /* module already initialized */
273 }
275 /* register with xdc.runtime to get a diags mask */
276 result = Registry_addModule(&Registry_CURDESC, MODULE_NAME);
277 Assert_isTrue(result == Registry_SUCCESS, (Assert_Id)NULL);
279 /* Log should be after the Registry_CURDESC is initialized */
280 Log_print1(Diags_ENTRY, "--> "FXNN": (remoteProcId=%d)",
281 (IArg)remoteProcId);
283 /* Gate to protect module object and lists: */
284 GateHwi_Params_init(&gatePrms);
285 module.gateH = GateHwi_create(&gatePrms, NULL);
287 /* Initialize Module State: */
288 for (i = 0; i < MAXMESSAGEQOBJECTS; i++) {
289 module.msgqObjects[i] = NULL;
290 }
292 HeapBuf_Params_init(&prms);
293 prms.blockSize = MSGBUFFERSIZE;
294 prms.numBlocks = MAXMESSAGEBUFFERS;
295 prms.buf = recv_buffers;
296 prms.bufSize = MAXHEAPSIZE;
297 prms.align = HEAPALIGNMENT;
298 module.heap = HeapBuf_create(&prms, NULL);
299 if (module.heap == 0) {
300 System_abort("RPMessage_init: HeapBuf_create returned 0\n");
301 }
303 Semaphore_Params_init(&semParams);
304 semParams.mode = Semaphore_Mode_BINARY;
305 transport.semHandle_toHost = Semaphore_create(0, &semParams, NULL);
307 isHost = (MultiProc_self() == MultiProc_getId("HOST"));
309 /* Initialize Transport related objects: */
311 VirtQueue_Params_init(&vqParams);
312 if (isHost) {
313 /* We don't handle this case currently! Host would need to prime vq. */
314 Assert_isTrue(FALSE, NULL);
315 }
316 else {
317 #if defined(OMAP5) || defined(VAYU)
318 vqParams.callback = callback_availBufReady;
319 #else
320 vqParams.callback = (xdc_Fxn)callback_availBufReady;
321 #endif
322 }
324 /*
325 * Create a pair VirtQueues (one for sending, one for receiving).
326 * Note: First one gets an even, second gets odd vq ID.
327 */
328 vqParams.vqId = ID_SELF_TO_HOST;
329 transport.virtQueue_toHost = (Ptr)VirtQueue_create(remoteProcId,
330 &vqParams, NULL);
331 vqParams.vqId = ID_HOST_TO_SELF;
332 transport.virtQueue_fromHost = (Ptr)VirtQueue_create(remoteProcId,
333 &vqParams, NULL);
335 /* Plug Vring Interrupts, and wait for host ready to recv kick: */
336 VirtQueue_startup(remoteProcId, isHost);
338 /* construct the Swi to process incoming messages: */
339 transport.swiHandle = Swi_create(RPMessage_swiFxn, NULL, NULL);
341 exit:
342 Log_print0(Diags_EXIT, "<-- "FXNN);
343 }
344 #undef FXNN
346 /*
347 * ======== MessasgeQCopy_finalize ========
348 */
349 #define FXNN "RPMessage_finalize"
350 Void RPMessage_finalize()
351 {
352 Log_print0(Diags_ENTRY, "--> "FXNN);
354 if (!curInit || --curInit) {
355 goto exit; /* module still in use, or uninitialized */
356 }
358 /* Tear down Module */
359 HeapBuf_delete(&(module.heap));
361 Swi_delete(&(transport.swiHandle));
363 GateHwi_delete(&module.gateH);
365 exit:
366 Log_print0(Diags_EXIT, "<-- "FXNN);
367 }
368 #undef FXNN
370 /*
371 * ======== RPMessage_create ========
372 */
373 #define FXNN "RPMessage_create"
374 RPMessage_Handle RPMessage_create(UInt32 reserved,
375 RPMessage_callback cb,
376 UArg arg,
377 UInt32 * endpoint)
378 {
379 RPMessage_Object *obj = NULL;
380 Bool found = FALSE;
381 Int i;
382 UInt16 queueIndex = 0;
383 IArg key;
385 Log_print4(Diags_ENTRY, "--> "FXNN": "
386 "(reserved=%d, cb=0x%x, arg=0x%x, endpoint=0x%x)",
387 (IArg)reserved, (IArg)cb, (IArg)arg, (IArg)endpoint);
389 Assert_isTrue((curInit > 0) , NULL);
391 key = GateHwi_enter(module.gateH);
393 if (reserved == RPMessage_ASSIGN_ANY) {
394 /* Search the array for a free slot above reserved: */
395 for (i = RPMessage_MAX_RESERVED_ENDPOINT + 1;
396 (i < MAXMESSAGEQOBJECTS) && (found == FALSE) ; i++) {
397 if (module.msgqObjects[i] == NULL) {
398 queueIndex = i;
399 found = TRUE;
400 break;
401 }
402 }
403 }
404 else if ((queueIndex = reserved) <= RPMessage_MAX_RESERVED_ENDPOINT) {
405 if (module.msgqObjects[queueIndex] == NULL) {
406 found = TRUE;
407 }
408 }
410 if (found) {
411 obj = Memory_alloc(NULL, sizeof(RPMessage_Object), 0, NULL);
412 if (obj != NULL) {
413 if (cb) {
414 /* Store callback and it's arg instead of semaphore: */
415 obj->cb = cb;
416 obj->arg= arg;
417 }
418 else {
419 obj->cb = NULL;
421 /* Allocate a semaphore to signal when messages received: */
422 obj->semHandle = Semaphore_create(0, NULL, NULL);
424 /* Create our queue of to be received messages: */
425 obj->queue = List_create(NULL, NULL);
426 }
428 /* Store our endpoint, and object: */
429 obj->queueId = queueIndex;
430 module.msgqObjects[queueIndex] = obj;
432 /* See RPMessage_unblock() */
433 obj->unblocked = FALSE;
435 *endpoint = queueIndex;
436 Log_print1(Diags_LIFECYCLE, FXNN": endPt created: %d",
437 (IArg)queueIndex);
438 }
439 }
441 GateHwi_leave(module.gateH, key);
443 Log_print1(Diags_EXIT, "<-- "FXNN": 0x%x", (IArg)obj);
444 return (obj);
445 }
446 #undef FXNN
448 /*
449 * ======== RPMessage_delete ========
450 */
451 #define FXNN "RPMessage_delete"
452 Int RPMessage_delete(RPMessage_Handle *handlePtr)
453 {
454 Int status = RPMessage_S_SUCCESS;
455 RPMessage_Object *obj;
456 Queue_elem *payload;
457 IArg key;
459 Log_print1(Diags_ENTRY, "--> "FXNN": (handlePtr=0x%x)", (IArg)handlePtr);
461 Assert_isTrue((curInit > 0) , NULL);
463 if (handlePtr && (obj = (RPMessage_Object *)(*handlePtr))) {
465 if (obj->cb) {
466 obj->cb = NULL;
467 obj->arg= NULL;
468 }
469 else {
470 Semaphore_delete(&(obj->semHandle));
472 /* Free/discard all queued message buffers: */
473 while ((payload = (Queue_elem *)List_get(obj->queue)) != NULL) {
474 HeapBuf_free(module.heap, (Ptr)payload, MSGBUFFERSIZE);
475 }
477 List_delete(&(obj->queue));
478 }
480 /* Null out our slot: */
481 key = GateHwi_enter(module.gateH);
482 module.msgqObjects[obj->queueId] = NULL;
483 GateHwi_leave(module.gateH, key);
485 Log_print1(Diags_LIFECYCLE, FXNN": endPt deleted: %d",
486 (IArg)obj->queueId);
488 /* Now free the obj */
489 Memory_free(NULL, obj, sizeof(RPMessage_Object));
491 *handlePtr = NULL;
492 }
494 Log_print1(Diags_EXIT, "<-- "FXNN": %d", (IArg)status);
495 return(status);
496 }
497 #undef FXNN
499 /*
500 * ======== RPMessage_recv ========
501 */
502 #define FXNN "RPMessage_recv"
503 Int RPMessage_recv(RPMessage_Handle handle, Ptr data, UInt16 *len,
504 UInt32 *rplyEndpt, UInt timeout)
505 {
506 Int status = RPMessage_S_SUCCESS;
507 RPMessage_Object *obj = (RPMessage_Object *)handle;
508 Bool semStatus;
509 Queue_elem *payload;
511 Log_print5(Diags_ENTRY, "--> "FXNN": (handle=0x%x, data=0x%x, len=0x%x,"
512 "rplyEndpt=0x%x, timeout=%d)", (IArg)handle, (IArg)data,
513 (IArg)len, (IArg)rplyEndpt, (IArg)timeout);
515 Assert_isTrue((curInit > 0) , NULL);
516 /* A callback was set: client should not be calling this fxn! */
517 Assert_isTrue((!obj->cb), NULL);
519 /* Check vring for pending messages before we block: */
520 Swi_post(transport.swiHandle);
522 /* Block until notified. */
523 semStatus = Semaphore_pend(obj->semHandle, timeout);
525 if (semStatus == FALSE) {
526 status = RPMessage_E_TIMEOUT;
527 Log_print0(Diags_STATUS, FXNN": Sem pend timeout!");
528 }
529 else if (obj->unblocked) {
530 status = RPMessage_E_UNBLOCKED;
531 }
532 else {
533 payload = (Queue_elem *)List_get(obj->queue);
534 Assert_isTrue((!payload), NULL);
535 }
537 if (status == RPMessage_S_SUCCESS) {
538 /* Now, copy payload to client and free our internal msg */
539 memcpy(data, payload->data, payload->len);
540 *len = payload->len;
541 *rplyEndpt = payload->src;
543 HeapBuf_free(module.heap, (Ptr)payload,
544 (payload->len + sizeof(Queue_elem)));
545 }
547 Log_print1(Diags_EXIT, "<-- "FXNN": %d", (IArg)status);
548 return (status);
549 }
550 #undef FXNN
552 /*
553 * ======== RPMessage_send ========
554 */
555 #define FXNN "RPMessage_send"
556 Int RPMessage_send(UInt16 dstProc,
557 UInt32 dstEndpt,
558 UInt32 srcEndpt,
559 Ptr data,
560 UInt16 len)
561 {
562 Int status = RPMessage_S_SUCCESS;
563 RPMessage_Object *obj;
564 Int16 token = 0;
565 RPMessage_Msg msg;
566 Queue_elem *payload;
567 UInt size;
568 IArg key;
569 int length;
571 Log_print5(Diags_ENTRY, "--> "FXNN": (dstProc=%d, dstEndpt=%d, "
572 "srcEndpt=%d, data=0x%x, len=%d", (IArg)dstProc, (IArg)dstEndpt,
573 (IArg)srcEndpt, (IArg)data, (IArg)len);
575 Assert_isTrue((curInit > 0) , NULL);
577 if (dstProc != MultiProc_self()) {
578 /* Send to remote processor: */
579 do {
580 token = VirtQueue_getAvailBuf(transport.virtQueue_toHost,
581 (Void **)&msg, &length);
582 } while (token < 0 && Semaphore_pend(transport.semHandle_toHost,
583 BIOS_WAIT_FOREVER));
584 if (token >= 0) {
585 /* Copy the payload and set message header: */
586 memcpy(msg->payload, data, len);
587 msg->dataLen = len;
588 msg->dstAddr = dstEndpt;
589 msg->srcAddr = srcEndpt;
590 msg->flags = 0;
591 msg->reserved = 0;
593 VirtQueue_addUsedBuf(transport.virtQueue_toHost, token,
594 RPMSG_BUF_SIZE);
595 VirtQueue_kick(transport.virtQueue_toHost);
596 }
597 else {
598 status = RPMessage_E_FAIL;
599 Log_print0(Diags_STATUS, FXNN": getAvailBuf failed!");
600 }
601 }
602 else {
603 /* Put on a Message queue on this processor: */
605 /* Protect from RPMessage_delete */
606 key = GateHwi_enter(module.gateH);
607 obj = module.msgqObjects[dstEndpt];
608 GateHwi_leave(module.gateH, key);
610 if (obj == NULL) {
611 Log_print1(Diags_STATUS, FXNN": no object for endpoint: %d",
612 (IArg)dstEndpt);
613 status = RPMessage_E_NOENDPT;
614 return status;
615 }
617 /* If callback registered, call it: */
618 if (obj->cb) {
619 Log_print2(Diags_INFO, FXNN": calling callback with data len: "
620 "%d, from: %d", len, srcEndpt);
621 obj->cb(obj, obj->arg, data, len, srcEndpt);
622 }
623 else {
624 /* else, put on a Message queue on this processor: */
625 /* Allocate a buffer to copy the payload: */
626 size = len + sizeof(Queue_elem);
628 /* HeapBuf_alloc() is non-blocking, so needs protection: */
629 key = GateHwi_enter(module.gateH);
630 payload = (Queue_elem *)HeapBuf_alloc(module.heap, size, 0, NULL);
631 GateHwi_leave(module.gateH, key);
633 if (payload != NULL) {
634 memcpy(payload->data, data, len);
635 payload->len = len;
636 payload->src = srcEndpt;
638 /* Put on the endpoint's queue and signal: */
639 List_put(obj->queue, (List_Elem *)payload);
640 Semaphore_post(obj->semHandle);
641 }
642 else {
643 status = RPMessage_E_MEMORY;
644 Log_print0(Diags_STATUS, FXNN": HeapBuf_alloc failed!");
645 }
646 }
647 }
649 Log_print1(Diags_EXIT, "<-- "FXNN": %d", (IArg)status);
650 return (status);
651 }
652 #undef FXNN
654 /*
655 * ======== RPMessage_unblock ========
656 */
657 #define FXNN "RPMessage_unblock"
658 Void RPMessage_unblock(RPMessage_Handle handle)
659 {
660 RPMessage_Object *obj = (RPMessage_Object *)handle;
662 Log_print1(Diags_ENTRY, "--> "FXNN": (handle=0x%x)", (IArg)handle);
664 Assert_isTrue((!obj->cb), NULL);
665 /* Set instance to 'unblocked' state, and post */
666 obj->unblocked = TRUE;
667 Semaphore_post(obj->semHandle);
668 Log_print0(Diags_EXIT, "<-- "FXNN);
669 }
670 #undef FXNN