blob: 47f83c6e017e98c7b7d1747196141f832fb8cfba [file] [log] [blame] [edit]
/*
* Copyright 2019, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: GPL-2.0-only
*/
#include <autoconf.h>
#include <string.h>
#include <pico_stack.h>
#include <pico_socket.h>
#include <pico_device.h>
#include <pico_addressing.h>
#include <pico_ipv4.h>
#undef PACKED
#include <sel4/sel4.h>
#include <utils/util.h>
#include <sel4utils/sel4_zf_logif.h>
#include "picoserver_client.h"
#include "picoserver_socket.h"
#include <picoserver_event.h>
#include <picoserver_peer.h>
#include <platsupport/io.h>
#include <picotcp-socket-sync.h>
#include <virtqueue.h>
#include <camkes/virtqueue.h>
/*
* Functions exposed by the connection from the interfaces.
*/
seL4_Word pico_control_get_sender_id(void);
void pico_control_emit(unsigned int);
int pico_control_largest_badge(void);
seL4_Word pico_recv_get_sender_id(void);
void *pico_recv_buf(seL4_Word);
size_t pico_recv_buf_size(seL4_Word);
seL4_Word pico_recv_enumerate_badge(unsigned int);
seL4_Word pico_send_get_sender_id(void);
void *pico_send_buf(seL4_Word);
size_t pico_send_buf_size(seL4_Word);
seL4_Word pico_send_enumerate_badge(unsigned int);
virtqueue_device_t tx_virtqueue;
virtqueue_device_t rx_virtqueue;
int num_clients;
int emit_client;
int emit_client_async;
/*
* Gets the client's ID and checks that it is valid.
*/
static inline seL4_Word client_check(void)
{
/* Client IDs start from one to avoid using the zero badge */
seL4_Word client_id = pico_control_get_sender_id();
ZF_LOGF_IF(client_id >= num_clients, "Client ID is greater than the number of clients registered!");
return client_id;
}
/*
* Performs the common operations in the various control RPC calls.
* This includes error checking and fetching the client's socket structure.
*/
static int server_control_common(seL4_Word client_id, int socket_fd, picoserver_socket_t **ret_socket)
{
if (socket_fd < 0) {
return -1;
}
*ret_socket = client_get_socket(client_id, socket_fd);
if (*ret_socket == NULL) {
return -1;
}
return 0;
}
/*
* Performs the common operations between the send and receive RPC calls.
* These include error checking, fetching the client's bookkeeping structures,
* their sockets etc.
*/
static int server_communication_common(seL4_Word client_id, int socket_fd, int len, int buffer_offset,
size_t buffer_size, picoserver_socket_t **ret_socket,
void **ret_buffer)
{
ZF_LOGF_IF(ret_socket == NULL || ret_buffer == NULL, "Passed in NULL for ret_socket or ret_buffer");
if (socket_fd < 0 || len < 0 || buffer_offset < 0) {
return -1;
}
if ((buffer_offset + len) > buffer_size) {
/* Make sure we don't overflow the buffer */
return -1;
}
*ret_socket = client_get_socket(client_id, socket_fd);
if (*ret_socket == NULL) {
return -1;
}
*ret_buffer += buffer_offset;
return 0;
}
static void tx_complete(void *cookie, int len);
static void rx_complete(void *cookie, int len);
static void tx_socket(picoserver_socket_t *client_socket);
static void rx_socket(picoserver_socket_t *client_socket);
static void tx_queue_handle(void);
static void rx_queue_handle(void);
static void socket_cb(uint16_t ev, struct pico_socket *s)
{
/* ZF_LOGE("\033[32mpico_socket addr = %x, ev = %d\033[0m", s, ev); */
/* Find the picoserver_socket struct that houses the pico_socket */
picoserver_socket_t *client_socket = client_get_socket_by_addr(s);
if (client_socket == NULL && s != NULL) {
/*
* For some reason, if pico_socket_listen is called to listen on
* a socket and when a client connects to the socket, PicoTCP will
* allocate another socket structure that will process callbacks even
* though we have not called pico_socket_accept. This results in a
* situation where we try to retrieve a socket from our hash table
* without having registed it in the first place. The solution now is
* to just ignore it and this shouldn't be a problem as PicoTCP does
* not allow interactions without accepting the client's connection.
*
*
*
* The dangling callback may also happen when the client calls
* pico_control_close instead of pico_control_shutdown +
* pico_control_close.
*/
return;
}
if (client_socket->async_transport) {
if (ev & PICO_SOCK_EV_RD) {
ev &= ~PICO_SOCK_EV_RD;
rx_queue_handle();
rx_socket(client_socket);
}
if (ev & PICO_SOCK_EV_WR) {
ev &= ~PICO_SOCK_EV_WR;
tx_queue_handle();
tx_socket(client_socket);
}
}
if (ev) {
seL4_Word client_id = client_socket->client_id;
int ret = client_put_event(client_id, client_socket->socket_fd, ev);
ZF_LOGF_IF(ret == -1, "Failed to set the event flags for client %"PRIuPTR"'s socket %d",
client_id + 1, client_socket->socket_fd);
emit_client = 1;
}
}
int pico_control_open(bool is_udp)
{
seL4_Word client_id = client_check();
picoserver_socket_t *new_socket = calloc(1, sizeof(picoserver_socket_t));
if (new_socket == NULL) {
ZF_LOGE("Failed to malloc memory for the picoserver struct");
return -1;
}
new_socket->client_id = client_id;
uint16_t protocol = (is_udp) ? PICO_PROTO_UDP : PICO_PROTO_TCP;
new_socket->socket = pico_socket_open(PICO_PROTO_IPV4, protocol, &socket_cb);
if (new_socket->socket == NULL) {
ZF_LOGE("Failed to open a new socket through picotcp");
free(new_socket);
return -1;
}
new_socket->protocol = protocol;
int ret = client_put_socket(client_id, new_socket);
if (ret == -1) {
ZF_LOGE("Failed to put the socket into the client's hash table");
pico_socket_close(new_socket->socket);
free(new_socket);
return -1;
}
new_socket->socket_fd = ret;
return ret;
}
int pico_control_bind(int socket_fd, uint32_t local_addr, uint16_t port)
{
seL4_Word client_id = client_check();
picoserver_socket_t *client_socket = NULL;
int ret = server_control_common(client_id, socket_fd, &client_socket);
if (ret) {
return -1;
}
port = short_be(port);
ret = pico_socket_bind(client_socket->socket, &local_addr, &port);
return ret;
}
int pico_control_connect(int socket_fd, uint32_t server_addr, uint16_t port)
{
seL4_Word client_id = client_check();
picoserver_socket_t *client_socket = NULL;
int ret = server_control_common(client_id, socket_fd, &client_socket);
if (ret) {
return -1;
}
port = short_be(port);
ret = pico_socket_connect(client_socket->socket, &server_addr, port);
return ret;
}
int pico_control_listen(int socket_fd, int backlog)
{
seL4_Word client_id = client_check();
picoserver_socket_t *client_socket = NULL;
int ret = server_control_common(client_id, socket_fd, &client_socket);
if (ret) {
return -1;
}
ret = pico_socket_listen(client_socket->socket, backlog);
return ret;
}
picoserver_peer_t pico_control_accept(int socket_fd)
{
seL4_Word client_id = client_check();
picoserver_peer_t peer = {0};
picoserver_socket_t *client_socket = NULL;
int ret = server_control_common(client_id, socket_fd, &client_socket);
if (ret) {
peer.result = -1;
return peer;
}
uint32_t peer_addr;
uint16_t remote_port;
struct pico_socket *socket = pico_socket_accept(client_socket->socket, &peer_addr, &remote_port);
if (socket == NULL) {
peer.result = -1;
return peer;
}
picoserver_socket_t *new_socket = calloc(1, sizeof(picoserver_socket_t));
if (new_socket == NULL) {
peer.result = -1;
pico_socket_close(socket);
return peer;
}
new_socket->client_id = client_id;
new_socket->socket = socket;
new_socket->protocol = PICO_PROTO_TCP;
ret = client_put_socket(client_id, new_socket);
if (ret == -1) {
peer.result = -1;
pico_socket_close(socket);
free(new_socket);
return peer;
}
new_socket->socket_fd = ret;
peer.result = 0;
peer.socket = ret;
peer.peer_addr = peer_addr;
peer.peer_port = remote_port;
return peer;
}
int pico_control_shutdown(int socket_fd, int mode)
{
seL4_Word client_id = client_check();
picoserver_socket_t *client_socket = NULL;
int ret = server_control_common(client_id, socket_fd, &client_socket);
if (ret) {
return -1;
}
ret = pico_socket_shutdown(client_socket->socket, mode);
return ret;
}
static void cleanup_async_socket(picoserver_socket_t *client_socket)
{
ZF_LOGF_IF(client_socket == NULL, "Invalid arg");
if (client_socket->async_transport) {
tx_msg_t *msg;
while (client_socket->async_transport->tx_pending_queue) {
ZF_LOGF_IF(client_socket->async_transport->tx_pending_queue_end == NULL, "Inconsistent queue state");
msg = client_socket->async_transport->tx_pending_queue;
msg->done_len = -1;
client_socket->async_transport->tx_pending_queue = msg->next;
if (client_socket->async_transport->tx_pending_queue_end == msg) {
client_socket->async_transport->tx_pending_queue_end = NULL;
}
tx_complete(msg->cookie_save, 0);
}
while (client_socket->async_transport->rx_pending_queue) {
ZF_LOGF_IF(client_socket->async_transport->rx_pending_queue_end == NULL, "Inconsistent queue state");
msg = client_socket->async_transport->rx_pending_queue;
msg->done_len = -1;
client_socket->async_transport->rx_pending_queue = msg->next;
if (client_socket->async_transport->rx_pending_queue_end == msg) {
client_socket->async_transport->rx_pending_queue_end = NULL;
}
rx_complete(msg->cookie_save, 0);
}
free(client_socket->async_transport);
client_socket->async_transport = NULL;
}
}
int pico_control_close(int socket_fd)
{
seL4_Word client_id = client_check();
picoserver_socket_t *client_socket = NULL;
int ret = server_control_common(client_id, socket_fd, &client_socket);
if (ret) {
return -1;
}
cleanup_async_socket(client_socket);
ret = client_delete_socket(client_id, socket_fd);
return ret;
}
int pico_control_set_async(int socket_fd, bool enabled)
{
seL4_Word client_id = client_check();
picoserver_socket_t *client_socket = NULL;
int ret = server_control_common(client_id, socket_fd, &client_socket);
if (ret) {
return -1;
}
if (enabled && (client_socket->async_transport == NULL)) {
client_socket->async_transport = calloc(1, sizeof(picoserver_socket_async_t));
if (client_socket->async_transport == NULL) {
ZF_LOGE("Failed to malloc memory for the picoserver async struct");
return -1;
}
} else if (!enabled && client_socket->async_transport) {
cleanup_async_socket(client_socket);
} else {
ZF_LOGW("pico_control_set_async called with no-op");
}
return 0;
}
picoserver_event_t pico_control_event_poll(void)
{
seL4_Word client_id = client_check();
/* Retrieve the client's outstanding events */
picoserver_event_t event = {0};
client_get_event(client_id, &event);
return event;
}
int pico_control_get_ipv4(uint32_t *addr)
{
struct pico_device *dev = pico_get_device("eth0");
if (dev == NULL) {
return -1;
}
*addr = pico_ipv4_link_by_dev(dev)->address.addr;
return 0;
}
int pico_send_write(int socket_fd, int len, int buffer_offset)
{
seL4_Word client_id = client_check();
/*
* client_id needs to be incremented here as the CAmkES generated interfaces are one off
* from ours
*/
size_t buffer_size = pico_send_buf_size(pico_send_enumerate_badge(client_id));
void *client_buf = pico_send_buf(pico_send_enumerate_badge(client_id));
picoserver_socket_t *client_socket = NULL;
int ret = server_communication_common(client_id, socket_fd, len, buffer_offset,
buffer_size, &client_socket, &client_buf);
if (ret) {
return -1;
}
ret = pico_socket_write(client_socket->socket, client_buf, len);
return ret;
}
int pico_send_send(int socket_fd, int len, int buffer_offset)
{
seL4_Word client_id = client_check();
/*
* client_id needs to be incremented here as the CAmkES generated interfaces are one off
* from ours
*/
size_t buffer_size = pico_send_buf_size(pico_send_enumerate_badge(client_id));
void *client_buf = pico_send_buf(pico_send_enumerate_badge(client_id));
picoserver_socket_t *client_socket = NULL;
int ret = server_communication_common(client_id, socket_fd, len, buffer_offset,
buffer_size, &client_socket, &client_buf);
if (ret) {
return -1;
}
ret = pico_socket_send(client_socket->socket, client_buf, len);
return ret;
}
int pico_send_sendto(int socket_fd, int len, int buffer_offset, uint32_t dst_addr, uint16_t remote_port)
{
seL4_Word client_id = client_check();
/*
* client_id needs to be incremented here as the CAmkES generated interfaces are one off
* from ours
*/
size_t buffer_size = pico_send_buf_size(pico_send_enumerate_badge(client_id));
void *client_buf = pico_send_buf(pico_send_enumerate_badge(client_id));
picoserver_socket_t *client_socket = NULL;
int ret = server_communication_common(client_id, socket_fd, len, buffer_offset,
buffer_size, &client_socket, &client_buf);
if (ret) {
return -1;
}
remote_port = short_be(remote_port);
ret = pico_socket_sendto(client_socket->socket, client_buf, len, &dst_addr, remote_port);
return ret;
}
int pico_recv_read(int socket_fd, int len, int buffer_offset)
{
seL4_Word client_id = client_check();
/*
* client_id needs to be incremented here as the CAmkES generated interfaces are one off
* from ours
*/
size_t buffer_size = pico_recv_buf_size(pico_recv_enumerate_badge(client_id));
void *client_buf = pico_recv_buf(pico_recv_enumerate_badge(client_id));
picoserver_socket_t *client_socket = NULL;
int ret = server_communication_common(client_id, socket_fd, len, buffer_offset,
buffer_size, &client_socket, &client_buf);
if (ret) {
return -1;
}
ret = pico_socket_read(client_socket->socket, client_buf, len);
return ret;
}
int pico_recv_recv(int socket_fd, int len, int buffer_offset)
{
seL4_Word client_id = client_check();
/*
* client_id needs to be incremented here as the CAmkES generated interfaces are one off
* from ours
*/
size_t buffer_size = pico_recv_buf_size(pico_recv_enumerate_badge(client_id));
void *client_buf = pico_recv_buf(pico_recv_enumerate_badge(client_id));
picoserver_socket_t *client_socket = NULL;
int ret = server_communication_common(client_id, socket_fd, len, buffer_offset,
buffer_size, &client_socket, &client_buf);
if (ret) {
return -1;
}
ret = pico_socket_recv(client_socket->socket, client_buf, len);
return ret;
}
int pico_recv_recvfrom(int socket_fd, int len, int buffer_offset, uint32_t *src_addr, uint16_t *remote_port)
{
seL4_Word client_id = client_check();
/*
* client_id needs to be incremented here as the CAmkES generated interfaces are one off
* from ours
*/
size_t buffer_size = pico_recv_buf_size(pico_recv_enumerate_badge(client_id));
void *client_buf = pico_recv_buf(pico_recv_enumerate_badge(client_id));
picoserver_socket_t *client_socket = NULL;
int ret = server_communication_common(client_id, socket_fd, len, buffer_offset,
buffer_size, &client_socket, &client_buf);
if (ret) {
return -1;
}
ret = pico_socket_recvfrom(client_socket->socket, client_buf, len, src_addr, remote_port);
/* Reverse the big endian port number */
*remote_port = short_be(*remote_port);
return ret;
}
static void notify_client(UNUSED seL4_Word badge, void *cookie)
{
if (emit_client) {
pico_control_emit(1);
emit_client = 0;
}
if (emit_client_async) {
tx_virtqueue.notify();
emit_client_async = 0;
}
}
static void tx_complete(void *cookie, int len)
{
virtqueue_ring_object_t handle;
handle.first = (uint32_t)(uintptr_t)cookie;
handle.cur = (uint32_t)(uintptr_t)cookie;
if (!virtqueue_add_used_buf(&tx_virtqueue, &handle, len)) {
ZF_LOGE("TX: Error while enqueuing available buffer");
}
emit_client_async = true;
}
static void tx_socket(picoserver_socket_t *client_socket)
{
if (client_socket == NULL || client_socket->socket == NULL) {
ZF_LOGE("Socket is null");
return;
}
if (client_socket->async_transport == NULL) {
ZF_LOGE("Socket isn't setup for async");
return;
}
while (client_socket->async_transport->tx_pending_queue) {
ZF_LOGF_IF(client_socket->async_transport->tx_pending_queue_end == NULL, "Inconsistent queue state");
int ret;
tx_msg_t *msg = client_socket->async_transport->tx_pending_queue;
if (client_socket->protocol == PICO_PROTO_UDP) {
ret = pico_socket_sendto(client_socket->socket, msg->buf + msg->done_len, msg->total_len - msg->done_len,
&msg->src_addr, msg->remote_port);
} else {
ret = pico_socket_send(client_socket->socket, msg->buf + msg->done_len, msg->total_len - msg->done_len);
}
if (ret == -1) {
/* Free the internal tx buffer in case tx fails. Up to the client to retry the trasmission */
ZF_LOGE("tx main: This shouldn't happen. Handle error case");
msg->done_len = -1;
client_socket->async_transport->tx_pending_queue = msg->next;
if (client_socket->async_transport->tx_pending_queue_end == msg) {
client_socket->async_transport->tx_pending_queue_end = NULL;
}
tx_complete(msg->cookie_save, 0);
continue;
}
if (ret < (msg->total_len - msg->done_len)) {
msg->done_len += ret;
return;
} else {
msg->done_len = msg->total_len;
client_socket->async_transport->tx_pending_queue = msg->next;
if (client_socket->async_transport->tx_pending_queue_end == msg) {
client_socket->async_transport->tx_pending_queue_end = NULL;
}
tx_complete(msg->cookie_save, msg->total_len);
}
}
}
static void tx_queue_handle(void)
{
while (1) {
virtqueue_ring_object_t handle;
if (virtqueue_get_available_buf(&tx_virtqueue, &handle) == 0) {
break;
}
void *buf;
unsigned len;
vq_flags_t flag;
int more = virtqueue_gather_available(&tx_virtqueue, &handle, &buf, &len, &flag);
if (more == 0) {
ZF_LOGE("No message received");
}
tx_msg_t *msg = DECODE_DMA_ADDRESS(buf);
ZF_LOGF_IF(msg == NULL, "msg is null");
ZF_LOGF_IF((msg->total_len > 1400) || (msg->total_len == 0), "bad msg len in tx %zd", msg->total_len);
picoserver_socket_t *client_socket = client_get_socket(0, msg->socket_fd);
if (client_socket == NULL || client_socket->socket == NULL) {
ZF_LOGE("Socket is null");
msg->done_len = -1;
tx_complete((void *)(uintptr_t)handle.first, 0);
continue;
}
if (client_socket->async_transport == NULL) {
ZF_LOGE("Socket isn't setup for async");
msg->done_len = -1;
tx_complete((void *)(uintptr_t)handle.first, 0);
continue;
}
if (client_socket->async_transport->tx_pending_queue) {
ZF_LOGF_IF(client_socket->async_transport->tx_pending_queue_end == NULL, "Inconsistent queue state");
client_socket->async_transport->tx_pending_queue_end->next = msg;
client_socket->async_transport->tx_pending_queue_end = msg;
msg->next = NULL;
msg->cookie_save = (void *)(uintptr_t)handle.first;
continue;
}
int ret;
if (client_socket->protocol == PICO_PROTO_UDP) {
ret = pico_socket_sendto(client_socket->socket, msg->buf, msg->total_len, &msg->src_addr, msg->remote_port);
} else {
ret = pico_socket_send(client_socket->socket, msg->buf, msg->total_len);
}
if (ret == -1) {
/* Free the internal tx buffer in case tx fails. Up to the client to retry the trasmission */
ZF_LOGE("tx main: This shouldn't happen. Handle error case: %d", pico_err);
msg->done_len = -1;
tx_complete((void *)(uintptr_t)handle.first, 0);
continue;
}
if (ret < msg->total_len) {
msg->done_len = ret;
msg->cookie_save = (void *)(uintptr_t)handle.first;
msg->next = NULL;
client_socket->async_transport->tx_pending_queue = msg;
client_socket->async_transport->tx_pending_queue_end = msg;
} else {
msg->done_len = msg->total_len;
tx_complete((void *)(uintptr_t)handle.first, msg->total_len);
}
}
}
static void rx_complete(void *cookie, int len)
{
virtqueue_ring_object_t handle;
handle.first = (uint32_t)(uintptr_t)cookie;
handle.cur = (uint32_t)(uintptr_t)cookie;
if (!virtqueue_add_used_buf(&rx_virtqueue, &handle, len)) {
ZF_LOGE("RX: Error while enqueuing available buffer");
}
emit_client_async = true;
}
static void rx_socket(picoserver_socket_t *client_socket)
{
if (client_socket == NULL || client_socket->socket == NULL) {
ZF_LOGE("Socket is null");
return;
}
if (client_socket->async_transport == NULL) {
ZF_LOGE("Socket isn't setup for async");
return;
}
while (client_socket->async_transport->rx_pending_queue) {
ZF_LOGF_IF(client_socket->async_transport->rx_pending_queue_end == NULL, "Inconsistent queue state");
int ret;
tx_msg_t *msg = client_socket->async_transport->rx_pending_queue;
if (client_socket->protocol == PICO_PROTO_UDP) {
ret = pico_socket_recvfrom(client_socket->socket, msg->buf + msg->done_len, msg->total_len - msg->done_len,
&msg->src_addr, &msg->remote_port);
} else {
ret = pico_socket_recv(client_socket->socket, msg->buf + msg->done_len, msg->total_len - msg->done_len);
}
if (ret == -1) {
/* Free the internal tx buffer in case tx fails. Up to the client to retry the trasmission */
ZF_LOGE("rx_socket: This shouldn't happen. Handle error case: %d", pico_err);
msg->done_len = -1;
client_socket->async_transport->rx_pending_queue = msg->next;
if (client_socket->async_transport->rx_pending_queue_end == msg) {
client_socket->async_transport->rx_pending_queue_end = NULL;
}
rx_complete(msg->cookie_save, 0);
return;
}
if ((client_socket->protocol == PICO_PROTO_TCP && ret < (msg->total_len - msg->done_len)) ||
(client_socket->protocol == PICO_PROTO_UDP && ret == 0)) {
msg->done_len += ret;
return;
} else {
msg->done_len += ret;
client_socket->async_transport->rx_pending_queue = msg->next;
if (client_socket->async_transport->rx_pending_queue_end == msg) {
client_socket->async_transport->rx_pending_queue_end = NULL;
}
rx_complete(msg->cookie_save, msg->total_len);
}
}
}
static void rx_queue_handle(void)
{
while (1) {
virtqueue_ring_object_t handle;
if (virtqueue_get_available_buf(&rx_virtqueue, &handle) == 0) {
break;
}
void *buf;
unsigned len;
vq_flags_t flag;
int more = virtqueue_gather_available(&rx_virtqueue, &handle, &buf, &len, &flag);
if (more == 0) {
ZF_LOGE("No message received");
}
tx_msg_t *msg = DECODE_DMA_ADDRESS(buf);
ZF_LOGF_IF(msg == NULL, "msg is null");
ZF_LOGF_IF((msg->total_len > 1400) || (msg->total_len == 0), "bad msg len in rx %zd", msg->total_len);
picoserver_socket_t *client_socket = client_get_socket(0, msg->socket_fd);
if (client_socket == NULL || client_socket->socket == NULL) {
ZF_LOGE("Socket is null");
msg->done_len = -1;
rx_complete((void *)(uintptr_t)handle.first, 0);
continue;
}
if (client_socket->async_transport == NULL) {
ZF_LOGE("Socket isn't setup for async");
msg->done_len = -1;
rx_complete((void *)(uintptr_t)handle.first, 0);
continue;
}
if (client_socket->async_transport->rx_pending_queue) {
ZF_LOGF_IF(client_socket->async_transport->rx_pending_queue_end == NULL, "Inconsistent queue state");
client_socket->async_transport->rx_pending_queue_end->next = msg;
client_socket->async_transport->rx_pending_queue_end = msg;
msg->next = NULL;
msg->cookie_save = (void *)(uintptr_t)handle.first;
continue;
}
int ret;
if (client_socket->protocol == PICO_PROTO_UDP) {
ret = pico_socket_recvfrom(client_socket->socket, msg->buf, msg->total_len, &msg->src_addr, &msg->remote_port);
} else {
ret = pico_socket_recv(client_socket->socket, msg->buf, msg->total_len);
}
if (ret == -1) {
/* Free the internal tx buffer in case tx fails. Up to the client to retry the trasmission */
ZF_LOGE("Picosocket_rx: This shouldn't happen. Handle error case");
msg->done_len = -1;
rx_complete((void *)(uintptr_t)handle.first, 0);
continue;
}
if ((client_socket->protocol == PICO_PROTO_TCP && ret < msg->total_len) ||
(client_socket->protocol == PICO_PROTO_UDP && ret == 0)) {
msg->done_len = ret;
msg->cookie_save = (void *)(uintptr_t)handle.first;
msg->next = NULL;
client_socket->async_transport->rx_pending_queue = msg;
client_socket->async_transport->rx_pending_queue_end = msg;
} else {
msg->done_len = ret;
rx_complete((void *)(uintptr_t)handle.first, msg->done_len);
}
}
}
static void tx_queue_handle_irq(seL4_Word badge, void *cookie)
{
rx_queue_handle();
tx_queue_handle();
pico_stack_tick();
}
int picotcp_socket_sync_server_init_late(register_callback_handler_fn_t callback_handler)
{
callback_handler(0, "notify_client", notify_client, NULL);
return 0;
}
int picotcp_socket_sync_server_init(ps_io_ops_t *io_ops, int num_clients_,
register_callback_handler_fn_t callback_handler)
{
num_clients = num_clients_;
picoserver_clients_init(num_clients);
seL4_Word tx_badge;
seL4_Word rx_badge;
int num_registered_virtqueues = camkes_virtqueue_channel_num();
if (num_registered_virtqueues < 2) {
/* Amount of virtqueues is less than expected */
return 0;
}
int tx_virtqueue_id = camkes_virtqueue_get_id_from_name("pico_tx");
int rx_virtqueue_id = camkes_virtqueue_get_id_from_name("pico_rx");
if (tx_virtqueue_id == -1 || rx_virtqueue_id == -1) {
/* We don't have the virtqueues we expect */
return 0;
}
/* Initialise read virtqueue */
int error = camkes_virtqueue_device_init_with_recv(&tx_virtqueue, (unsigned) tx_virtqueue_id,
NULL, &tx_badge);
if (error) {
ZF_LOGE("Unable to initialise serial server read virtqueue");
}
/* Initialise write virtqueue */
error = camkes_virtqueue_device_init_with_recv(&rx_virtqueue, (unsigned) rx_virtqueue_id,
NULL, &rx_badge);
if (error) {
ZF_LOGE("Unable to initialise serial server write virtqueue");
}
error = callback_handler(tx_badge, "client_event_handler", tx_queue_handle_irq, NULL);
if (error) {
ZF_LOGE("Unable to register handler");
}
return 0;
}