| /* | |
| * Copyright 2019, Data61, CSIRO (ABN 41 687 119 230) | |
| * | |
| * SPDX-License-Identifier: BSD-2-Clause | |
| */ | |
| #include <autoconf.h> | |
| #include <camkes.h> | |
| #include <stdio.h> | |
| #include <stdint.h> | |
| #include <string.h> | |
| #include <sel4/sel4.h> | |
| #include <utils/attribute.h> | |
| #include <utils/ansi.h> | |
| #include <camkes.h> | |
| #include <virtqueue.h> | |
| #include <camkes/virtqueue.h> | |
| #include <platsupport/chardev.h> | |
| #include "plat.h" | |
| #include "server_virtqueue.h" | |
| virtqueue_device_t read_virtqueue; | |
| virtqueue_device_t write_virtqueue; | |
| int write_in_progress; | |
| int read_in_progress; | |
| enum virtqueue_op { | |
| VQ_SERIAL_READ, | |
| VQ_SERIAL_WRITE | |
| }; | |
| typedef struct { | |
| virtqueue_device_t *vq; | |
| virtqueue_ring_object_t virtqueue_handle; | |
| void *serial_buf; | |
| size_t buf_size; | |
| } virtqueue_token_t; | |
| virtqueue_token_t current_read_vq_token; | |
| virtqueue_token_t current_write_vq_token; | |
| static void write_callback(ps_chardevice_t *device, enum chardev_status stat, | |
| size_t bytes_transfered, void *token) | |
| { | |
| virtqueue_token_t *vq_token = token; | |
| void *serial_buf = vq_token->serial_buf; | |
| if (!serial_buf) { | |
| return; | |
| } | |
| virtqueue_device_t *vq = vq_token->vq; | |
| vq->notify(); | |
| free(serial_buf); | |
| vq_token->serial_buf = NULL; | |
| write_in_progress = 0; | |
| } | |
| static void read_callback(ps_chardevice_t *device, enum chardev_status stat, | |
| size_t bytes_transfered, void *token) | |
| { | |
| virtqueue_token_t *vq_token = token; | |
| void *serial_buf = vq_token->serial_buf; | |
| size_t buf_size = vq_token->buf_size; | |
| if (!serial_buf) { | |
| return; | |
| } | |
| virtqueue_device_t *vq = vq_token->vq; | |
| virtqueue_ring_object_t handle = vq_token->virtqueue_handle; | |
| if (camkes_virtqueue_device_scatter_copy_buffer(vq, &handle, serial_buf, buf_size) < 0) { | |
| ZF_LOGE("Unable to copy read data to virtqueue"); | |
| } | |
| vq->notify(); | |
| free(serial_buf); | |
| vq_token->serial_buf = NULL; | |
| read_in_progress = 0; | |
| } | |
| static void handle_virtqueue_message(virtqueue_device_t *vq, virtqueue_ring_object_t *handle, enum virtqueue_op op) | |
| { | |
| void *serial_buffer; | |
| size_t buf_size; | |
| buf_size = virtqueue_scattered_available_size(vq, handle); | |
| if (!(serial_buffer = calloc(buf_size, sizeof(char)))) { | |
| ZF_LOGE("Unable to alloc serial buffer of size: %zu", buf_size); | |
| virtqueue_add_used_buf(vq, handle, 0); | |
| return; | |
| } | |
| if (op == VQ_SERIAL_READ) { | |
| current_read_vq_token.vq = vq; | |
| current_read_vq_token.virtqueue_handle = *handle; | |
| current_read_vq_token.serial_buf = serial_buffer; | |
| current_read_vq_token.buf_size = buf_size; | |
| if (plat_serial_read(serial_buffer, buf_size, read_callback, ¤t_read_vq_token) < 0) { | |
| ZF_LOGE("Unable to serial read buffer"); | |
| free(serial_buffer); | |
| virtqueue_add_used_buf(vq, handle, 0); | |
| } | |
| } else { | |
| int err; | |
| current_write_vq_token.vq = vq; | |
| current_write_vq_token.virtqueue_handle = *handle; | |
| current_write_vq_token.serial_buf = serial_buffer; | |
| current_write_vq_token.buf_size = buf_size; | |
| camkes_virtqueue_device_gather_copy_buffer(vq, handle, serial_buffer, buf_size); | |
| if ((err = plat_serial_write(serial_buffer, buf_size, write_callback, ¤t_write_vq_token)) < 0) { | |
| ZF_LOGE("Unable to serial write buffer: %d", err); | |
| free(serial_buffer); | |
| virtqueue_add_used_buf(vq, handle, 0); | |
| } | |
| } | |
| return; | |
| } | |
| static void handle_virtqueue_callback(virtqueue_device_t *vq, enum virtqueue_op op) | |
| { | |
| virtqueue_ring_object_t handle; | |
| if (!virtqueue_get_available_buf(vq, &handle)) { | |
| ZF_LOGE("Serial server virtqueue dequeue failed"); | |
| return; | |
| } | |
| /* Process the incoming virtqueue message */ | |
| handle_virtqueue_message(vq, &handle, op); | |
| } | |
| void serial_read_wait_callback(void) | |
| { | |
| int UNUSED error; | |
| error = serial_lock(); | |
| if (VQ_DEV_POLL(&read_virtqueue)) { | |
| read_in_progress = 1; | |
| handle_virtqueue_callback(&read_virtqueue, VQ_SERIAL_READ); | |
| } | |
| error = serial_unlock(); | |
| } | |
| void serial_write_wait_callback(void) | |
| { | |
| int UNUSED error; | |
| error = serial_lock(); | |
| if (VQ_DEV_POLL(&write_virtqueue)) { | |
| write_in_progress = 1; | |
| handle_virtqueue_callback(&write_virtqueue, VQ_SERIAL_WRITE); | |
| } | |
| error = serial_unlock(); | |
| } | |
| int virtqueue_init(void) | |
| { | |
| /* Initialise read virtqueue */ | |
| int error = camkes_virtqueue_device_init(&read_virtqueue, 0); | |
| if (error) { | |
| ZF_LOGE("Unable to initialise serial server read virtqueue"); | |
| } | |
| /* Initialise write virtqueue */ | |
| error = camkes_virtqueue_device_init(&write_virtqueue, 1); | |
| if (error) { | |
| ZF_LOGE("Unable to initialise serial server write virtqueue"); | |
| } | |
| return error; | |
| } |