[ipc/ipcdev.git] / qnx / src / ipc3x_dev / ti / syslink / ipc / hlos / knl / transports / virtio / VirtQueue.c
1 /*
2 * Copyright (c) 2011-2015, 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 VirtQueue.c
34 *
35 * @brief Virtio Queue implementation for BIOS
36 *
37 * Differences between BIOS version and Linux kernel (include/linux/virtio.h):
38 * - Renamed module from virtio.h to VirtQueue_Object.h to match the API prefixes;
39 * - BIOS (XDC) types and CamelCasing used;
40 * - virtio_device concept removed (i.e, assumes no containing device);
41 * - simplified scatterlist from Linux version;
42 * - VirtQueue_Objects are created statically here, so just added a VirtQueue_Object_init()
43 * fxn to take the place of the Virtio vring_new_virtqueue() API;
44 * - The notify function is implicit in the implementation, and not provided
45 * by the client, as it is in Linux virtio.
46 *
47 * All VirtQueue operations can be called in any context.
48 *
49 * The virtio header should be included in an application as follows:
50 * @code
51 * #include <ti/ipc/rpmsg/VirtQueue.h>
52 * @endcode
53 *
54 */
56 #include <ti/syslink/Std.h>
57 #include <ti/syslink/utils/Memory.h>
58 #include <ti/syslink/utils/Trace.h>
60 #include <_ArchIpcInt.h>
61 #include <ArchIpcInt.h>
62 #include "VirtQueue.h"
64 #include <ti/ipc/MultiProc.h>
65 #include <ti/syslink/ProcMgr.h>
67 #include <ti/syslink/utils/String.h>
69 #include "virtio_ring.h"
70 #include "_rpmsg.h"
71 #include <ipu_pm.h>
72 #include <pthread.h>
74 /* Used for defining the size of the virtqueue registry */
75 #define NUM_QUEUES 2
77 typedef struct VirtQueue_Object {
78 /* Id for this VirtQueue_Object */
79 UInt16 id;
81 /* The function to call when buffers are consumed (can be NULL) */
82 VirtQueue_callback callback;
84 /* Shared state */
85 struct vring vring;
87 /* Number of free buffers */
88 UInt16 num_free;
90 /* Last available index; updated by VirtQueue_getUsedBuf */
91 UInt16 last_used_idx;
93 /* Will eventually be used to kick remote processor */
94 UInt16 procId;
96 /* Interrupt Id for kicking remote processor */
97 UInt16 intId;
99 /* Local virtual address for vring struct */
100 UInt32 vaddr;
102 /* Physical address for vring struct */
103 UInt32 paddr;
105 /* Private arg from user */
106 void * arg;
108 /* Mutex to protect vrings from multi-thread access */
109 pthread_mutex_t mutex;
110 } VirtQueue_Object;
112 static UInt numQueues = 0;
113 static struct VirtQueue_Object *queueRegistry[MultiProc_MAXPROCESSORS][NUM_QUEUES];
115 static UInt32 coreIntId[MultiProc_MAXPROCESSORS];
117 static inline Void * mapPAtoVA(VirtQueue_Handle vq, UInt pa)
118 {
119 UInt offset = vq->paddr - pa;
120 return (Void *)(vq->vaddr - offset);
121 }
123 static inline UInt mapVAtoPA(VirtQueue_Handle vq, Void * va)
124 {
125 UInt offset = vq->vaddr - (UInt)va;
126 return (UInt)(vq->paddr - offset);
127 }
129 /*!
130 * ======== VirtQueue_cb ========
131 */
132 Void VirtQueue_cb(Void *buf, VirtQueue_Handle vq)
133 {
134 if (vq/* && vq->cb_enabled*/) {
135 /* Call the registered vq callback */
136 vq->callback(vq, vq->arg);
137 }
138 }
140 /*!
141 * ======== VirtQueue_kick ========
142 */
143 Void VirtQueue_kick(VirtQueue_Handle vq)
144 {
145 /*
146 * We need to expose available array entries before sending an
147 * interrupt.
148 */
149 asm(" DSB ST");
151 GT_2trace(curTrace, GT_2CLASS,
152 "VirtQueue_kick: Sending interrupt to proc %d with payload 0x%x",
153 vq->procId, vq->id);
155 #if defined (IPC_USE_IPU_PM)
156 ipu_pm_restore_ctx(vq->procId);
157 #endif
158 ArchIpcInt_sendInterrupt(vq->procId, vq->intId, vq->id);
159 }
161 /*!
162 * ======== VirtQueue_addUsedBufAddr ========
163 */
164 Int VirtQueue_addUsedBufAddr(VirtQueue_Handle vq, Void *buf, UInt32 len)
165 {
166 struct vring_used_elem *used = NULL;
167 UInt16 head = 0;
168 Int status = 0;
170 if ((head > vq->vring.num) || (head < 0)) {
171 status = -1;
172 GT_setFailureReason (curTrace,
173 GT_4CLASS,
174 "VirtQueue_addUsedBuf",
175 status,
176 "head is invalid!");
177 }
178 else {
179 /*
180 * The virtqueue contains a ring of used buffers. Get a pointer to the
181 * next entry in that used ring.
182 */
183 head = vq->vring.used->idx % vq->vring.num;
184 vq->vring.desc[head].addr = mapVAtoPA(vq, buf);
185 vq->vring.desc[head].len = len;
186 vq->vring.desc[head].flags = 0;
187 used = &vq->vring.used->ring[head];
188 used->id = head;
189 used->len = len;
191 vq->vring.used->idx++;
192 }
194 return status;
195 }
197 /*!
198 * ======== VirtQueue_addAvailBuf ========
199 */
200 Int VirtQueue_addAvailBuf(VirtQueue_Handle vq, Void *buf, UInt32 len, Int16 head)
201 {
202 UInt16 avail;
204 pthread_mutex_lock(&vq->mutex);
206 if (vq->num_free == 0) {
207 /* There's no more space */
208 GT_setFailureReason (curTrace,
209 GT_4CLASS,
210 "VirtQueue_addAvailBuf",
211 (-1), // TODO: Make this a valid error code
212 "no more space!");
214 }
215 else {
216 vq->num_free--;
218 avail = vq->vring.avail->idx % vq->vring.num;
219 vq->vring.avail->ring[avail] = head;
221 vq->vring.desc[head].addr = mapVAtoPA(vq, buf);
222 vq->vring.desc[head].len = len;
223 vq->vring.desc[head].flags = 0;
225 /*
226 * Descriptors and available array need to be set before we expose the
227 * new available array entries.
228 */
229 asm(" DMB ST");
231 vq->vring.avail->idx++;
232 }
234 pthread_mutex_unlock(&vq->mutex);
236 return (vq->num_free);
237 }
239 /*!
240 * ======== VirtQueue_getUsedBuf ========
241 */
242 Int16 VirtQueue_getUsedBuf(VirtQueue_Object *vq, Void **buf)
243 {
244 UInt16 head;
246 pthread_mutex_lock(&vq->mutex);
248 /* There's nothing available? */
249 if (vq->last_used_idx == vq->vring.used->idx) {
250 /* We need to know about added buffers */
251 vq->vring.avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT;
253 pthread_mutex_unlock(&vq->mutex);
255 return (-1);
256 }
258 /* Only get used array entries after they have been exposed. */
259 asm(" DMB");
261 /* No need to know be kicked about added buffers anymore */
262 //vq->vring.avail->flags |= VRING_AVAIL_F_NO_INTERRUPT; // disabling for now, since there seems to be a race condition where an M3->A9 message is not detected because the interrupt isn't sent.
264 head = vq->vring.used->ring[vq->last_used_idx % vq->vring.num].id;
265 vq->last_used_idx++;
266 vq->num_free++;
268 pthread_mutex_unlock(&vq->mutex);
270 *buf = mapPAtoVA(vq, vq->vring.desc[head].addr);
272 return (head);
273 }
275 /*!
276 * ======== VirtQueue_disableCallback ========
277 */
278 Void VirtQueue_disableCallback(VirtQueue_Handle vq)
279 {
280 //TODO
281 GT_0trace(curTrace, GT_3CLASS, "VirtQueue_disableCallback not supported.");
282 }
284 /*!
285 * ======== VirtQueue_enableCallback ========
286 */
287 Bool VirtQueue_enableCallback(VirtQueue_Handle vq)
288 {
289 GT_0trace(curTrace, GT_3CLASS, "VirtQueue_enableCallback not supported.");
291 //TODO
292 return (FALSE);
293 }
295 /*!
296 * @brief This function implements the interrupt service routine for the
297 * interrupt received from the remote cores.
298 *
299 * @param refData object to be handled in ISR
300 *
301 * @sa VirtQueue_cb
302 */
303 static
304 Bool
305 VirtQueue_ISR (UInt32 msg, Void * arg)
306 {
307 UInt32 procId = (UInt32)arg;
308 ProcMgr_Handle handle = NULL;
309 Int status = 0;
311 GT_2trace (curTrace, GT_ENTER, "_VirtQueue_ISR", msg, arg);
313 /* Interrupt clear is done by ArchIpcInt. */
315 switch(msg) {
316 case (UInt)RP_MBOX_ECHO_REPLY:
317 Osal_printf ("Echo reply from %s",
318 MultiProc_getName(procId));
319 break;
321 case (UInt)RP_MBOX_CRASH:
322 Osal_printf ("Crash notification for %s",
323 MultiProc_getName(procId));
324 status = ProcMgr_open(&handle, procId);
325 if (status >= 0) {
326 ProcMgr_setState(handle, ProcMgr_State_Error);
327 ProcMgr_close(&handle);
328 }
329 else {
330 Osal_printf("Failed to open ProcMgr handle");
331 }
332 break;
334 case (UInt)RP_MBOX_BOOTINIT_DONE:
335 Osal_printf ("Got BootInit done from %s",
336 MultiProc_getName(procId));
337 // TODO: What to do with this message?
338 break;
340 case (UInt)RP_MBOX_HIBERNATION_ACK:
341 Osal_printf ("Got Hibernation ACK from %s",
342 MultiProc_getName(procId));
343 break;
345 case (UInt)RP_MBOX_HIBERNATION_CANCEL:
346 Osal_printf ("Got Hibernation CANCEL from %s",
347 MultiProc_getName(procId));
348 break;
350 default:
351 /*
352 * If the message isn't one of the above, it's a virtqueue
353 * message
354 */
355 if (msg%2 < NUM_QUEUES) {
356 /* This message is for us! */
357 VirtQueue_cb((void *)msg, queueRegistry[procId][msg%2]);
358 }
359 break;
360 }
362 return TRUE;
363 }
365 /*!
366 * ======== VirtQueue_create ========
367 */
368 VirtQueue_Handle VirtQueue_create (VirtQueue_callback callback, UInt16 procId,
369 UInt16 id, UInt32 vaddr, UInt32 paddr,
370 UInt32 num, UInt32 align, Void *arg)
371 {
372 VirtQueue_Object *vq = NULL;
374 vq = Memory_alloc(NULL, sizeof(VirtQueue_Object), 0, NULL);
375 if (!vq) {
376 return (NULL);
377 }
379 vq->callback = callback;
380 vq->id = id;
381 numQueues++;
382 vq->procId = procId;
383 vq->intId = coreIntId[procId];
384 vq->arg = arg;
386 /* init the vring */
387 vring_init(&(vq->vring), num, (void *)vaddr, align);
389 vq->num_free = num;
390 vq->last_used_idx = 0;
391 vq->vaddr = vaddr;
392 vq->paddr = paddr;
394 vq->vring.avail->idx = 0;
395 vq->vring.used->idx = 0;
397 /* Initialize the flags */
398 vq->vring.avail->flags = 0;
399 vq->vring.used->flags = 0;
401 /* Store the VirtQueue locally */
402 if (queueRegistry[procId][vq->id%2] == NULL)
403 queueRegistry[procId][vq->id%2] = vq;
404 else {
405 Osal_printf ("VirtQueue ID %d already created", id);
406 Memory_free(NULL, vq, sizeof(VirtQueue_Object));
407 vq = NULL;
408 }
410 /* Initialize mutex */
411 pthread_mutex_init(&vq->mutex, NULL);
413 return (vq);
414 }
416 /*!
417 * ======== VirtQueue_delete ========
418 */
419 Int VirtQueue_delete (VirtQueue_Handle * vq)
420 {
421 VirtQueue_Object * obj = (VirtQueue_Object *)(*vq);
422 /* Store the VirtQueue locally */
423 queueRegistry[obj->procId][obj->id%2] = NULL;
425 /* Destroy mutex */
426 pthread_mutex_destroy(&obj->mutex);
428 Memory_free(NULL, obj, sizeof(VirtQueue_Object));
429 *vq = NULL;
430 numQueues--;
432 return 0;
433 }
435 /*!
436 * ======== VirtQueue_startup ========
437 */
438 Void VirtQueue_startup(UInt16 procId, UInt32 intId, UInt32 paddr)
439 {
440 Int32 status = 0;
441 UInt32 arg = procId;
443 coreIntId[procId] = intId;
445 /* Register for interrupts with CORE0, CORE1 messages come through CORE0 */
446 status = ArchIpcInt_interruptRegister (procId,
447 intId,
448 VirtQueue_ISR, (Ptr)arg);
449 if (status >= 0) {
450 /* Notify the remote proc that the mbox is ready */
451 status = ArchIpcInt_sendInterrupt (procId,
452 intId,
453 RP_MBOX_READY);
454 }
455 }
457 /*!
458 * ======== VirtQueue_startup ========
459 */
460 Void VirtQueue_destroy(UInt16 procId)
461 {
462 Int32 status = 0;
463 Int i = 0;
465 for (i = 0; i < NUM_QUEUES; i++) {
466 if (queueRegistry[procId][i]) {
467 VirtQueue_delete(&queueRegistry[procId][i]);
468 queueRegistry[procId][i] = NULL;
469 }
470 }
472 /* Un-register for interrupts with CORE0 */
473 status = ArchIpcInt_interruptUnregister (procId);
474 if (status < 0) {
475 GT_setFailureReason (curTrace,
476 GT_4CLASS,
477 "VirtQueue_destroy",
478 status,
479 "ArchIpcInt_interruptUnregister failed");
480 }
481 }