aos/lib/aos/ump_net_client.c
2022-05-27 19:11:22 +02:00

208 lines
6.6 KiB
C

#include <aos/aos.h>
#include <aos/deferred.h>
#include <aos/aos_rpc.h>
#include <aos/ump_binding.h>
#include <aos/ump_chan.h>
#include <aos/ump_net.h>
#include <aos/ump_net_client.h>
#include <collections/hash_table.h>
struct udp_listen_entry {
ump_net_udp_recv_handler_t recv_handler;
void *recv_handler_arg;
};
static bool net_initialized = false;
static struct ump_send_chan *net_send_chan;
static struct ump_recv_chan *net_recv_chan;
static struct ump_net_call *call_queue_head = NULL;
static struct ump_net_call *call_queue_tail = NULL;
static collections_hash_table* udp_listen_table;
static void net_recv_next (void);
static void handle_net_header_recv (void *arg, size_t header_size, void *header_raw, size_t payload_size);
errval_t ump_net_init (struct waitset *ws) {
if (net_initialized) return LIB_ERR_NET_ALREADY_INIT;
net_initialized = true;
errval_t err;
struct aos_rpc *rpc = aos_rpc_get_init_channel();
struct capref net_cap;
for (int attempts = 0; attempts < 60; attempts++) {
err = aos_rpc_ump_connect(rpc, UMP_SERVER_NET, &net_cap);
if (err != LIB_ERR_UMP_NOT_REGISTERED) break;
barrelfish_usleep(500000);
}
if (err_is_fail(err)) return err;
// Create the two uni-directional channels
err = ump_chan_init(UMP_ROLE_CLIENT, &net_send_chan, &net_recv_chan,
sizeof(struct ump_net_in_header), net_cap, ws);
if (err_is_fail(err)) return err;
net_recv_next();
collections_hash_create_with_buckets(&udp_listen_table, 100, free);
return SYS_ERR_OK;
}
static void net_recv_next (void) {
ump_recv_header(net_recv_chan, handle_net_header_recv, NULL);
}
static void net_recv_ignore_payload (void *arg, size_t payload_size, void *payload) {
net_recv_next();
}
// This is called twice, once from the send callback, once from the receive
// handler. We do it this way because we don't know in which order these
// two events happen.
static void ump_net_op_callback (void *arg, struct ump_send_queue_entry *entry) {
struct ump_net_call *call = arg;
if (call->half_done == false) {
call->half_done = true;
return;
}
if (call->in_header.op == UMP_NET_OP_UDP_SEND) {
((ump_net_udp_send_callback_t)call->cb)(
call->cb_arg, call->in_header.ret_err
);
} else if (call->in_header.op == UMP_NET_OP_UDP_LISTEN) {
((ump_net_udp_listen_callback_t)call->cb)(
call->cb_arg, call->in_header.ret_err,
call->in_header.d.udp_listen.dest_port
);
} else if (call->in_header.op == UMP_NET_OP_UDP_LISTEN_STOP) {
((ump_net_udp_listen_stop_callback_t)call->cb)(
call->cb_arg, call->in_header.ret_err
);
}
}
static void handle_net_header_recv (void *arg, size_t header_size, void *header_raw, size_t payload_size) {
struct ump_net_in_header *header = header_raw;
if (header->op == UMP_NET_EV_UDP_RECV) {
struct udp_listen_entry *entry = collections_hash_find(udp_listen_table, header->d.udp_recv.dest_port);
if (entry != NULL) {
entry->recv_handler(entry->recv_handler_arg, &header->d.udp_recv, payload_size);
return;
}
} else {
assert(call_queue_head != NULL);
assert(call_queue_head->out_header.op == header->op);
call_queue_head->in_header = *header;
ump_net_op_callback(call_queue_head, NULL);
call_queue_head = call_queue_head->next;
if (call_queue_head == NULL) call_queue_tail = NULL;
}
ump_recv_payload(net_recv_chan, NULL, net_recv_ignore_payload, NULL);
}
static void ump_net_send_call (
struct ump_net_call *call,
size_t payload_size, const void *payload,
void *cb, void *cb_arg
) {
call->half_done = false;
call->cb = cb;
call->cb_arg = cb_arg;
if (call_queue_head == NULL) {
call_queue_head = call;
} else {
call_queue_tail->next = call;
}
call_queue_tail = call;
ump_send(net_send_chan, &call->ump_entry,
sizeof(struct ump_net_out_header), &call->out_header,
payload_size, payload,
ump_net_op_callback, call);
}
void ump_net_udp_send (
struct ump_net_call *call,
uint32_t dest_ip,
uint16_t src_port,
uint16_t dest_port,
size_t payload_size, const void *payload,
ump_net_udp_send_callback_t cb, void *cb_arg
) {
call->out_header = (struct ump_net_out_header){
.op = UMP_NET_OP_UDP_SEND,
.d = { .udp_send = {
.dest_ip = dest_ip,
.src_port = src_port,
.dest_port = dest_port,
} }
};
ump_net_send_call(call, payload_size, payload, cb, cb_arg);
}
void ump_net_udp_listen (
struct ump_net_call *call,
uint16_t dest_port,
ump_net_udp_recv_handler_t recv_handler, void *recv_handler_arg,
ump_net_udp_listen_callback_t cb, void *cb_arg
) {
if (collections_hash_find(udp_listen_table, dest_port) != NULL) {
cb(cb_arg, LIB_ERR_NET_PORT_IN_USE, 0);
return;
}
struct udp_listen_entry *entry = malloc(sizeof(struct udp_listen_entry));
if (entry == NULL) {
cb(cb_arg, LIB_ERR_MALLOC_FAIL, 0);
return;
}
entry->recv_handler = recv_handler;
entry->recv_handler_arg = recv_handler_arg;
collections_hash_insert(udp_listen_table, dest_port, entry);
call->out_header = (struct ump_net_out_header){
.op = UMP_NET_OP_UDP_LISTEN,
.d = { .udp_listen = { .dest_port = dest_port } }
};
ump_net_send_call(call, 0, NULL, cb, cb_arg);
}
void ump_net_udp_listen_stop (
struct ump_net_call *call,
uint16_t dest_port,
ump_net_udp_listen_stop_callback_t cb, void *cb_arg
) {
if (collections_hash_find(udp_listen_table, dest_port) == NULL) {
cb(cb_arg, LIB_ERR_NET_NOT_LISTENING);
return;
}
collections_hash_delete(udp_listen_table, dest_port);
call->out_header = (struct ump_net_out_header){
.op = UMP_NET_OP_UDP_LISTEN_STOP,
.d = { .udp_listen_stop = { .dest_port = dest_port } }
};
ump_net_send_call(call, 0, NULL, cb, cb_arg);
}
static ump_recv_payload_callback_fn_t recv_payload_callback;
static void *recv_payload_callback_arg;
static void recv_payload_callback_wrap (void *arg, size_t payload_size, void *payload) {
if (recv_payload_callback != NULL) {
recv_payload_callback(recv_payload_callback_arg, payload_size, payload);
}
net_recv_next();
}
void ump_net_recv_payload (
void *payload,
ump_recv_payload_callback_fn_t callback, void *callback_arg
) {
recv_payload_callback = callback;
recv_payload_callback_arg = callback_arg;
ump_recv_payload(net_recv_chan, payload, recv_payload_callback_wrap, NULL);
}