1/*
2 * Copyright 2018, Data61, CSIRO (ABN 41 687 119 230)
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <camkes.h>
8#include <stdio.h>
9#include <unistd.h>
10#include <string.h>
11#include <virtqueue.h>
12#include <camkes/virtqueue.h>
13#include <utils/util.h>
14
15#define COUNT_LIMIT 20
16
17/* Message to be passed around  clients*/
18typedef struct {
19    int val;
20} payload_t;
21
22/* Each component has a send and recv virtqueue */
23virtqueue_driver_t send_virtqueue;
24virtqueue_device_t recv_virtqueue;
25
26void handle_send_callback(void);
27
28void send_payload_message(int val)
29{
30    payload_t data;
31    data.val = val;
32
33    void *alloc_buffer = NULL;
34
35    /* First check if there is data still waiting in the send virtqueue
36     * We need to flush the virtqueue before sending data on it
37     */
38    if (VQ_DRV_POLL(&send_virtqueue)) {
39        handle_send_callback();
40    }
41
42    int err = camkes_virtqueue_buffer_alloc(&send_virtqueue, &alloc_buffer, sizeof(payload_t));
43    if (err) {
44        ZF_LOGE("Client send buffer allocation failed");
45        return;
46    }
47
48    memcpy(alloc_buffer, (void *)&data, sizeof(payload_t));
49
50    if (camkes_virtqueue_driver_send_buffer(&send_virtqueue, alloc_buffer, sizeof(payload_t)) != 0) {
51        camkes_virtqueue_buffer_free(&send_virtqueue, alloc_buffer);
52        return;
53    }
54    send_virtqueue.notify();
55}
56
57void handle_virtqueue_message(void *buffer)
58{
59    payload_t *data = (payload_t *)buffer;
60
61    printf("Client %d recieved payload value: %d\n", client_id, data->val);
62
63    /* Finish the message passing once we reach COUNT_LIMIT */
64    if (data->val == COUNT_LIMIT) {
65        return;
66    }
67
68    /* Forward the message with an incremented count */
69    send_payload_message(data->val + 1);
70}
71
72void handle_recv_callback(void)
73{
74    void *available_buff = NULL;
75    size_t buf_size = 0;
76    vq_flags_t flag;
77    virtqueue_ring_object_t handle;
78    if (!virtqueue_get_available_buf(&recv_virtqueue, &handle)) {
79        ZF_LOGE("Client virtqueue dequeue failed");
80        return;
81    }
82
83    while (!camkes_virtqueue_device_gather_buffer(&recv_virtqueue, &handle, &available_buff, &buf_size, &flag)) {
84        /* Process the incoming virtqueue message */
85        handle_virtqueue_message(available_buff);
86    }
87
88    if (!virtqueue_add_used_buf(&recv_virtqueue, &handle, 0)) {
89        ZF_LOGE("Unable to enqueue used recv buffer");
90        return;
91    }
92
93    recv_virtqueue.notify();
94
95}
96
97void handle_send_callback(void)
98{
99    void *send_buff = NULL;
100    size_t buf_size = 0;
101    uint32_t wr_len = 0;
102    vq_flags_t flag;
103    virtqueue_ring_object_t handle;
104    if (!virtqueue_get_used_buf(&send_virtqueue, &handle, &wr_len)) {
105        ZF_LOGE("Client send dequeue failed");
106        return;
107    }
108    while (!camkes_virtqueue_driver_gather_buffer(&send_virtqueue, &handle, &send_buff, &buf_size, &flag)) {
109        /* Clean up and free the buffer we allocated */
110        camkes_virtqueue_buffer_free(&send_virtqueue, send_buff);
111    }
112}
113
114void virtqueue_wait_callback(void)
115{
116    if (VQ_DEV_POLL(&recv_virtqueue)) {
117        handle_recv_callback();
118    }
119
120    if (VQ_DRV_POLL(&send_virtqueue)) {
121        handle_send_callback();
122    }
123}
124
125int run()
126{
127    /* Initialise send virtqueue */
128    int err = camkes_virtqueue_driver_init(&send_virtqueue, 0);
129    if (err) {
130        ZF_LOGE("Unable to initialise client send virtqueue");
131        return 1;
132    }
133
134    /* Initialise recv virtqueue */
135    err = camkes_virtqueue_device_init(&recv_virtqueue, 1);
136    if (err) {
137        ZF_LOGE("Unable to initialise client recv virtqueue");
138        return 1;
139    }
140
141    /* Client 0 will send the first message */
142    if (client_id == 0) {
143        printf("Client 0 sending first message\n");
144        send_payload_message(1);
145    }
146
147    timeout_periodic(0, 1000 * NS_IN_MS);
148    seL4_Word badge;
149    seL4_CPtr notification = timeout_notification();
150    while (1) {
151        seL4_Wait(notification, &badge);
152    }
153}
154