HelenOS sources
This source file includes following definitions.
- default_client_data_constructor
- default_client_data_destructor
- async_set_client_data_constructor
- async_set_client_data_destructor
- client_key_hash
- client_hash
- client_key_equal
- async_client_get
- async_client_put
- connection_fibril
- async_get_label
- async_new_connection
- async_create_callback_port
- notification_key_hash
- notification_hash
- notification_key_equal
- route_call
- notification_fibril_func
- async_spawn_notification_handler
- queue_notification
- notification_create
- async_irq_subscribe
- async_irq_unsubscribe
- async_event_subscribe
- async_event_task_subscribe
- async_event_unmask
- async_event_task_unmask
- async_get_call_timeout
- async_get_call
- async_get_client_data
- async_get_client_data_by_id
- async_put_client_data_by_id
- handle_call
- async_manager_worker
- async_manager_fibril
- async_create_manager
- __async_server_init
- __async_server_fini
- async_accept_0
- async_answer_0
- async_answer_1
- async_answer_2
- async_answer_3
- async_answer_4
- async_answer_5
- async_forward_fast
- async_forward_slow
- async_forward_0
- async_forward_1
- async_forward_2
- async_forward_3
- async_forward_4
- async_forward_5
- async_connect_to_me
- async_share_in_receive
- async_share_in_finalize
- async_share_out_receive
- async_share_out_finalize
- async_data_read_receive
- async_data_read_finalize
- async_data_read_forward_fast
- async_data_read_forward_0_0
- async_data_read_forward_1_0
- async_data_read_forward_2_0
- async_data_read_forward_3_0
- async_data_read_forward_4_0
- async_data_read_forward_0_1
- async_data_read_forward_1_1
- async_data_read_forward_2_1
- async_data_read_forward_3_1
- async_data_read_forward_4_1
- async_data_write_receive
- async_data_write_finalize
- async_data_write_accept
- async_data_write_void
- async_data_write_forward_fast
- async_data_write_forward_0_0
- async_data_write_forward_1_0
- async_data_write_forward_2_0
- async_data_write_forward_3_0
- async_data_write_forward_4_0
- async_data_write_forward_0_1
- async_data_write_forward_1_1
- async_data_write_forward_2_1
- async_data_write_forward_3_1
- async_data_write_forward_4_1
- async_callback_receive
- async_callback_receive_start
- async_state_change_receive
- async_state_change_finalize
- async_manager
#define _LIBC_ASYNC_C_
#include <ipc/ipc.h>
#include <async.h>
#include "../private/async.h"
#undef _LIBC_ASYNC_C_
#include <ipc/irq.h>
#include <ipc/event.h>
#include <fibril.h>
#include <adt/hash_table.h>
#include <adt/hash.h>
#include <adt/list.h>
#include <assert.h>
#include <errno.h>
#include <time.h>
#include <stdbool.h>
#include <stdlib.h>
#include <mem.h>
#include <macros.h>
#include <str_error.h>
#include <as.h>
#include <abi/mm/as.h>
#include "../private/libc.h"
#include "../private/fibril.h"
#define DPRINTF(...) ((void) 0)
typedef struct {
ht_link_t link;
task_id_t in_task_id;
int refcnt;
void *data;
} client_t;
typedef struct {
fid_t fid;
ht_link_t link;
task_id_t in_task_id;
client_t *client;
mpsc_t *msg_channel;
ipc_call_t call;
async_port_handler_t handler;
void *data;
} connection_t;
typedef struct {
link_t link;
ipc_call_t calldata;
} notification_msg_t;
typedef struct {
ht_link_t htlink;
link_t qlink;
sysarg_t imethod;
async_notification_handler_t handler;
void *arg;
list_t msg_list;
} notification_t;
static fibril_local connection_t *fibril_connection;
static void *default_client_data_constructor(void)
{
return NULL;
}
static void default_client_data_destructor(void *data)
{
}
static async_client_data_ctor_t async_client_data_create =
default_client_data_constructor;
static async_client_data_dtor_t async_client_data_destroy =
default_client_data_destructor;
void async_set_client_data_constructor(async_client_data_ctor_t ctor)
{
assert(async_client_data_create == default_client_data_constructor);
async_client_data_create = ctor;
}
void async_set_client_data_destructor(async_client_data_dtor_t dtor)
{
assert(async_client_data_destroy == default_client_data_destructor);
async_client_data_destroy = dtor;
}
static fibril_rmutex_t client_mutex;
static hash_table_t client_hash_table;
static fibril_rmutex_t notification_mutex;
static hash_table_t notification_hash_table;
static LIST_INITIALIZE(notification_queue);
static FIBRIL_SEMAPHORE_INITIALIZE(notification_semaphore, 0);
static LIST_INITIALIZE(notification_freelist);
static long notification_freelist_total = 0;
static long notification_freelist_used = 0;
static sysarg_t notification_avail = 0;
static size_t client_key_hash(const void *key)
{
const task_id_t *in_task_id = key;
return *in_task_id;
}
static size_t client_hash(const ht_link_t *item)
{
client_t *client = hash_table_get_inst(item, client_t, link);
return client_key_hash(&client->in_task_id);
}
static bool client_key_equal(const void *key, const ht_link_t *item)
{
const task_id_t *in_task_id = key;
client_t *client = hash_table_get_inst(item, client_t, link);
return *in_task_id == client->in_task_id;
}
static const hash_table_ops_t client_hash_table_ops = {
.hash = client_hash,
.key_hash = client_key_hash,
.key_equal = client_key_equal,
.equal = NULL,
.remove_callback = NULL
};
static client_t *async_client_get(task_id_t client_id, bool create)
{
client_t *client = NULL;
fibril_rmutex_lock(&client_mutex);
ht_link_t *link = hash_table_find(&client_hash_table, &client_id);
if (link) {
client = hash_table_get_inst(link, client_t, link);
client->refcnt++;
} else if (create) {
client = malloc(sizeof(client_t));
if (client) {
client->in_task_id = client_id;
client->data = async_client_data_create();
client->refcnt = 1;
hash_table_insert(&client_hash_table, &client->link);
}
}
fibril_rmutex_unlock(&client_mutex);
return client;
}
static void async_client_put(client_t *client)
{
bool destroy;
fibril_rmutex_lock(&client_mutex);
if (--client->refcnt == 0) {
hash_table_remove(&client_hash_table, &client->in_task_id);
destroy = true;
} else
destroy = false;
fibril_rmutex_unlock(&client_mutex);
if (destroy) {
if (client->data)
async_client_data_destroy(client->data);
free(client);
}
}
static errno_t connection_fibril(void *arg)
{
assert(arg);
fibril_connection = (connection_t *) arg;
mpsc_t *c = fibril_connection->msg_channel;
client_t *client = async_client_get(fibril_connection->in_task_id, true);
if (!client) {
ipc_answer_0(fibril_connection->call.cap_handle, ENOMEM);
goto out;
}
fibril_connection->client = client;
fibril_connection->handler(&fibril_connection->call,
fibril_connection->data);
async_client_put(client);
mpsc_close(c);
ipc_call_t call;
while (mpsc_receive(c, &call, NULL) == EOK)
ipc_answer_0(call.cap_handle, EHANGUP);
out:
mpsc_destroy(c);
free(fibril_connection);
return EOK;
}
sysarg_t async_get_label(void)
{
return (sysarg_t) fibril_connection;
}
static fid_t async_new_connection(connection_t *conn, task_id_t in_task_id,
ipc_call_t *call, async_port_handler_t handler, void *data)
{
conn->in_task_id = in_task_id;
conn->msg_channel = mpsc_create(sizeof(ipc_call_t));
conn->handler = handler;
conn->data = data;
if (!conn->msg_channel)
goto error;
if (call)
conn->call = *call;
else
conn->call.cap_handle = CAP_NIL;
conn->fid = fibril_create(connection_fibril, conn);
if (conn->fid == 0)
goto error;
fibril_start(conn->fid);
return conn->fid;
error:
if (conn->msg_channel)
mpsc_destroy(conn->msg_channel);
free(conn);
if (call)
ipc_answer_0(call->cap_handle, ENOMEM);
return (fid_t) NULL;
}
errno_t async_create_callback_port(async_exch_t *exch, iface_t iface, sysarg_t arg1,
sysarg_t arg2, async_port_handler_t handler, void *data, port_id_t *port_id)
{
if ((iface & IFACE_MOD_CALLBACK) != IFACE_MOD_CALLBACK)
return EINVAL;
if (exch == NULL)
return ENOENT;
connection_t *conn = calloc(1, sizeof(*conn));
if (!conn)
return ENOMEM;
ipc_call_t answer;
aid_t req = async_send_5(exch, IPC_M_CONNECT_TO_ME, iface, arg1, arg2,
0, (sysarg_t) conn, &answer);
errno_t rc;
async_wait_for(req, &rc);
if (rc != EOK) {
free(conn);
return rc;
}
rc = async_create_port_internal(iface, handler, data, port_id);
if (rc != EOK) {
free(conn);
return rc;
}
fid_t fid = async_new_connection(conn, answer.task_id, NULL, handler,
data);
if (fid == (fid_t) NULL)
return ENOMEM;
return EOK;
}
static size_t notification_key_hash(const void *key)
{
const sysarg_t *id = key;
return *id;
}
static size_t notification_hash(const ht_link_t *item)
{
notification_t *notification =
hash_table_get_inst(item, notification_t, htlink);
return notification_key_hash(¬ification->imethod);
}
static bool notification_key_equal(const void *key, const ht_link_t *item)
{
const sysarg_t *id = key;
notification_t *notification =
hash_table_get_inst(item, notification_t, htlink);
return *id == notification->imethod;
}
static const hash_table_ops_t notification_hash_table_ops = {
.hash = notification_hash,
.key_hash = notification_key_hash,
.key_equal = notification_key_equal,
.equal = NULL,
.remove_callback = NULL
};
static errno_t route_call(ipc_call_t *call)
{
assert(call);
connection_t *conn = (connection_t *) call->request_label;
if (!conn)
return ENOENT;
assert(conn->msg_channel);
errno_t rc = mpsc_send(conn->msg_channel, call);
if (ipc_get_imethod(call) == IPC_M_PHONE_HUNGUP) {
mpsc_close(conn->msg_channel);
}
return rc;
}
static errno_t notification_fibril_func(void *arg)
{
(void) arg;
while (true) {
fibril_semaphore_down(¬ification_semaphore);
fibril_rmutex_lock(¬ification_mutex);
assert(!list_empty(¬ification_queue));
notification_t *notification = list_get_instance(
list_first(¬ification_queue), notification_t, qlink);
async_notification_handler_t handler = notification->handler;
void *arg = notification->arg;
notification_msg_t *m = list_pop(¬ification->msg_list,
notification_msg_t, link);
assert(m);
ipc_call_t calldata = m->calldata;
notification_freelist_used--;
if (notification_freelist_total > 64 &&
notification_freelist_total > 2 * notification_freelist_used) {
notification_freelist_total--;
} else {
list_append(&m->link, ¬ification_freelist);
m = NULL;
}
if (list_empty(¬ification->msg_list))
list_remove(¬ification->qlink);
fibril_rmutex_unlock(¬ification_mutex);
if (handler)
handler(&calldata, arg);
free(m);
}
return EOK;
}
errno_t async_spawn_notification_handler(void)
{
fid_t f = fibril_create(notification_fibril_func, NULL);
if (f == 0)
return ENOMEM;
fibril_add_ready(f);
return EOK;
}
static void queue_notification(ipc_call_t *call)
{
assert(call);
fibril_rmutex_lock(¬ification_mutex);
notification_msg_t *m = list_pop(¬ification_freelist,
notification_msg_t, link);
if (!m) {
fibril_rmutex_unlock(¬ification_mutex);
m = malloc(sizeof(notification_msg_t));
if (!m) {
DPRINTF("Out of memory.\n");
abort();
}
fibril_rmutex_lock(¬ification_mutex);
notification_freelist_total++;
}
sysarg_t imethod = ipc_get_imethod(call);
ht_link_t *link = hash_table_find(¬ification_hash_table, &imethod);
if (!link) {
notification_freelist_total--;
fibril_rmutex_unlock(¬ification_mutex);
free(m);
return;
}
notification_t *notification =
hash_table_get_inst(link, notification_t, htlink);
notification_freelist_used++;
m->calldata = *call;
list_append(&m->link, ¬ification->msg_list);
if (!link_in_use(¬ification->qlink))
list_append(¬ification->qlink, ¬ification_queue);
fibril_rmutex_unlock(¬ification_mutex);
fibril_semaphore_up(¬ification_semaphore);
}
static notification_t *notification_create(async_notification_handler_t handler, void *arg)
{
notification_t *notification = calloc(1, sizeof(notification_t));
if (!notification)
return NULL;
notification->handler = handler;
notification->arg = arg;
list_initialize(¬ification->msg_list);
fid_t fib = 0;
fibril_rmutex_lock(¬ification_mutex);
if (notification_avail == 0) {
fib = fibril_create(notification_fibril_func, NULL);
if (fib == 0) {
fibril_rmutex_unlock(¬ification_mutex);
free(notification);
return NULL;
}
}
sysarg_t imethod = notification_avail;
notification_avail++;
notification->imethod = imethod;
hash_table_insert(¬ification_hash_table, ¬ification->htlink);
fibril_rmutex_unlock(¬ification_mutex);
if (imethod == 0) {
assert(fib);
fibril_add_ready(fib);
}
return notification;
}
errno_t async_irq_subscribe(int inr, async_notification_handler_t handler,
void *data, const irq_code_t *ucode, cap_irq_handle_t *handle)
{
notification_t *notification = notification_create(handler, data);
if (!notification)
return ENOMEM;
cap_irq_handle_t ihandle;
errno_t rc = ipc_irq_subscribe(inr, notification->imethod, ucode,
&ihandle);
if (rc == EOK && handle != NULL) {
*handle = ihandle;
}
return rc;
}
errno_t async_irq_unsubscribe(cap_irq_handle_t ihandle)
{
return ipc_irq_unsubscribe(ihandle);
}
errno_t async_event_subscribe(event_type_t evno,
async_notification_handler_t handler, void *data)
{
notification_t *notification = notification_create(handler, data);
if (!notification)
return ENOMEM;
return ipc_event_subscribe(evno, notification->imethod);
}
errno_t async_event_task_subscribe(event_task_type_t evno,
async_notification_handler_t handler, void *data)
{
notification_t *notification = notification_create(handler, data);
if (!notification)
return ENOMEM;
return ipc_event_task_subscribe(evno, notification->imethod);
}
errno_t async_event_unmask(event_type_t evno)
{
return ipc_event_unmask(evno);
}
errno_t async_event_task_unmask(event_task_type_t evno)
{
return ipc_event_task_unmask(evno);
}
bool async_get_call_timeout(ipc_call_t *call, usec_t usecs)
{
assert(call);
assert(fibril_connection);
struct timespec ts;
struct timespec *expires = NULL;
if (usecs) {
getuptime(&ts);
ts_add_diff(&ts, USEC2NSEC(usecs));
expires = &ts;
}
errno_t rc = mpsc_receive(fibril_connection->msg_channel,
call, expires);
if (rc == ETIMEOUT)
return false;
if (rc != EOK) {
memset(call, 0, sizeof(ipc_call_t));
ipc_set_imethod(call, IPC_M_PHONE_HUNGUP);
call->cap_handle = CAP_NIL;
}
return true;
}
bool async_get_call(ipc_call_t *call)
{
return async_get_call_timeout(call, 0);
}
void *async_get_client_data(void)
{
assert(fibril_connection);
return fibril_connection->client->data;
}
void *async_get_client_data_by_id(task_id_t client_id)
{
client_t *client = async_client_get(client_id, false);
if (!client)
return NULL;
if (!client->data) {
async_client_put(client);
return NULL;
}
return client->data;
}
void async_put_client_data_by_id(task_id_t client_id)
{
client_t *client = async_client_get(client_id, false);
assert(client);
assert(client->data);
async_client_put(client);
async_client_put(client);
}
static void handle_call(ipc_call_t *call)
{
assert(call);
if (call->flags & IPC_CALL_ANSWERED) {
async_reply_received(call);
return;
}
if (call->cap_handle == CAP_NIL) {
if (call->flags & IPC_CALL_NOTIF) {
queue_notification(call);
}
return;
}
if (ipc_get_imethod(call) == IPC_M_CONNECT_ME_TO) {
connection_t *conn = calloc(1, sizeof(*conn));
if (!conn) {
ipc_answer_0(call->cap_handle, ENOMEM);
return;
}
iface_t iface = (iface_t) ipc_get_arg1(call);
void *data;
async_port_handler_t handler =
async_get_port_handler(iface, 0, &data);
async_new_connection(conn, call->task_id, call, handler, data);
return;
}
errno_t rc = route_call(call);
if (rc == EOK)
return;
if (call->cap_handle != CAP_NIL)
ipc_answer_0(call->cap_handle, EHANGUP);
}
static errno_t async_manager_worker(void)
{
ipc_call_t call;
errno_t rc;
while (true) {
rc = fibril_ipc_wait(&call, NULL);
if (rc == EOK)
handle_call(&call);
}
return 0;
}
static errno_t async_manager_fibril(void *arg)
{
async_manager_worker();
return 0;
}
static fid_t async_create_manager(void)
{
fid_t fid = fibril_create_generic(async_manager_fibril, NULL, PAGE_SIZE);
fibril_start(fid);
return fid;
}
void __async_server_init(void)
{
if (fibril_rmutex_initialize(&client_mutex) != EOK)
abort();
if (fibril_rmutex_initialize(¬ification_mutex) != EOK)
abort();
if (!hash_table_create(&client_hash_table, 0, 0, &client_hash_table_ops))
abort();
if (!hash_table_create(¬ification_hash_table, 0, 0,
¬ification_hash_table_ops))
abort();
async_create_manager();
}
void __async_server_fini(void)
{
fibril_rmutex_destroy(&client_mutex);
fibril_rmutex_destroy(¬ification_mutex);
}
errno_t async_accept_0(ipc_call_t *call)
{
cap_call_handle_t chandle = call->cap_handle;
assert(chandle != CAP_NIL);
call->cap_handle = CAP_NIL;
return ipc_answer_5(chandle, EOK, 0, 0, 0, 0, async_get_label());
}
errno_t async_answer_0(ipc_call_t *call, errno_t retval)
{
cap_call_handle_t chandle = call->cap_handle;
assert(chandle != CAP_NIL);
call->cap_handle = CAP_NIL;
return ipc_answer_0(chandle, retval);
}
errno_t async_answer_1(ipc_call_t *call, errno_t retval, sysarg_t arg1)
{
cap_call_handle_t chandle = call->cap_handle;
assert(chandle != CAP_NIL);
call->cap_handle = CAP_NIL;
return ipc_answer_1(chandle, retval, arg1);
}
errno_t async_answer_2(ipc_call_t *call, errno_t retval, sysarg_t arg1,
sysarg_t arg2)
{
cap_call_handle_t chandle = call->cap_handle;
assert(chandle != CAP_NIL);
call->cap_handle = CAP_NIL;
return ipc_answer_2(chandle, retval, arg1, arg2);
}
errno_t async_answer_3(ipc_call_t *call, errno_t retval, sysarg_t arg1,
sysarg_t arg2, sysarg_t arg3)
{
cap_call_handle_t chandle = call->cap_handle;
assert(chandle != CAP_NIL);
call->cap_handle = CAP_NIL;
return ipc_answer_3(chandle, retval, arg1, arg2, arg3);
}
errno_t async_answer_4(ipc_call_t *call, errno_t retval, sysarg_t arg1,
sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
{
cap_call_handle_t chandle = call->cap_handle;
assert(chandle != CAP_NIL);
call->cap_handle = CAP_NIL;
return ipc_answer_4(chandle, retval, arg1, arg2, arg3, arg4);
}
errno_t async_answer_5(ipc_call_t *call, errno_t retval, sysarg_t arg1,
sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5)
{
cap_call_handle_t chandle = call->cap_handle;
assert(chandle != CAP_NIL);
call->cap_handle = CAP_NIL;
return ipc_answer_5(chandle, retval, arg1, arg2, arg3, arg4, arg5);
}
static errno_t async_forward_fast(ipc_call_t *call, async_exch_t *exch,
sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, unsigned int mode)
{
assert(call);
cap_call_handle_t chandle = call->cap_handle;
assert(chandle != CAP_NIL);
call->cap_handle = CAP_NIL;
if (exch == NULL)
return ENOENT;
return ipc_forward_fast(chandle, exch->phone, imethod, arg1, arg2,
mode);
}
static errno_t async_forward_slow(ipc_call_t *call, async_exch_t *exch,
sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, sysarg_t arg3,
sysarg_t arg4, sysarg_t arg5, unsigned int mode)
{
assert(call);
cap_call_handle_t chandle = call->cap_handle;
assert(chandle != CAP_NIL);
call->cap_handle = CAP_NIL;
if (exch == NULL)
return ENOENT;
return ipc_forward_slow(chandle, exch->phone, imethod, arg1, arg2, arg3,
arg4, arg5, mode);
}
errno_t async_forward_0(ipc_call_t *call, async_exch_t *exch, sysarg_t imethod,
unsigned int mode)
{
return async_forward_fast(call, exch, imethod, 0, 0, mode);
}
errno_t async_forward_1(ipc_call_t *call, async_exch_t *exch, sysarg_t imethod,
sysarg_t arg1, unsigned int mode)
{
return async_forward_fast(call, exch, imethod, arg1, 0, mode);
}
errno_t async_forward_2(ipc_call_t *call, async_exch_t *exch, sysarg_t imethod,
sysarg_t arg1, sysarg_t arg2, unsigned int mode)
{
return async_forward_fast(call, exch, imethod, arg1, arg2, mode);
}
errno_t async_forward_3(ipc_call_t *call, async_exch_t *exch, sysarg_t imethod,
sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, unsigned int mode)
{
return async_forward_slow(call, exch, imethod, arg1, arg2, arg3, 0, 0,
mode);
}
errno_t async_forward_4(ipc_call_t *call, async_exch_t *exch, sysarg_t imethod,
sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
unsigned int mode)
{
return async_forward_slow(call, exch, imethod, arg1, arg2, arg3, arg4,
0, mode);
}
errno_t async_forward_5(ipc_call_t *call, async_exch_t *exch, sysarg_t imethod,
sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4, sysarg_t arg5,
unsigned int mode)
{
return async_forward_slow(call, exch, imethod, arg1, arg2, arg3, arg4,
arg5, mode);
}
errno_t async_connect_to_me(async_exch_t *exch, iface_t iface, sysarg_t arg2,
sysarg_t arg3)
{
if (exch == NULL)
return ENOENT;
sysarg_t label = 0;
errno_t rc = async_req_5_0(exch, IPC_M_CONNECT_TO_ME, iface, arg2, arg3,
0, label);
return rc;
}
bool async_share_in_receive(ipc_call_t *call, size_t *size)
{
assert(call);
assert(size);
async_get_call(call);
if (ipc_get_imethod(call) != IPC_M_SHARE_IN)
return false;
*size = (size_t) ipc_get_arg1(call);
return true;
}
errno_t async_share_in_finalize(ipc_call_t *call, void *src, unsigned int flags)
{
assert(call);
cap_call_handle_t chandle = call->cap_handle;
assert(chandle != CAP_NIL);
call->cap_handle = CAP_NIL;
return ipc_answer_2(chandle, EOK, (sysarg_t) src, (sysarg_t) flags);
}
bool async_share_out_receive(ipc_call_t *call, size_t *size,
unsigned int *flags)
{
assert(call);
assert(size);
assert(flags);
async_get_call(call);
if (ipc_get_imethod(call) != IPC_M_SHARE_OUT)
return false;
*size = (size_t) ipc_get_arg2(call);
*flags = (unsigned int) ipc_get_arg3(call);
return true;
}
errno_t async_share_out_finalize(ipc_call_t *call, void **dst)
{
assert(call);
cap_call_handle_t chandle = call->cap_handle;
assert(chandle != CAP_NIL);
call->cap_handle = CAP_NIL;
return ipc_answer_2(chandle, EOK, (sysarg_t) __progsymbols.end,
(sysarg_t) dst);
}
bool async_data_read_receive(ipc_call_t *call, size_t *size)
{
assert(call);
async_get_call(call);
if (ipc_get_imethod(call) != IPC_M_DATA_READ)
return false;
if (size)
*size = (size_t) ipc_get_arg2(call);
return true;
}
errno_t async_data_read_finalize(ipc_call_t *call, const void *src, size_t size)
{
assert(call);
cap_call_handle_t chandle = call->cap_handle;
assert(chandle != CAP_NIL);
call->cap_handle = CAP_NIL;
return ipc_answer_2(chandle, EOK, (sysarg_t) src, (sysarg_t) size);
}
static errno_t async_data_read_forward_fast(async_exch_t *exch, sysarg_t imethod,
sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
ipc_call_t *dataptr)
{
if (exch == NULL)
return ENOENT;
ipc_call_t call;
if (!async_data_read_receive(&call, NULL)) {
async_answer_0(&call, EINVAL);
return EINVAL;
}
aid_t msg = async_send_4(exch, imethod, arg1, arg2, arg3, arg4,
dataptr);
if (msg == 0) {
async_answer_0(&call, EINVAL);
return EINVAL;
}
errno_t retval = ipc_forward_fast(call.cap_handle, exch->phone, 0, 0, 0,
IPC_FF_ROUTE_FROM_ME);
if (retval != EOK) {
async_forget(msg);
async_answer_0(&call, retval);
return retval;
}
errno_t rc;
async_wait_for(msg, &rc);
return (errno_t) rc;
}
errno_t async_data_read_forward_0_0(async_exch_t *exch, sysarg_t imethod)
{
return async_data_read_forward_fast(exch, imethod, 0, 0, 0, 0, NULL);
}
errno_t async_data_read_forward_1_0(async_exch_t *exch, sysarg_t imethod,
sysarg_t arg1)
{
return async_data_read_forward_fast(exch, imethod, arg1, 0, 0, 0, NULL);
}
errno_t async_data_read_forward_2_0(async_exch_t *exch, sysarg_t imethod,
sysarg_t arg1, sysarg_t arg2)
{
return async_data_read_forward_fast(exch, imethod, arg1, arg2, 0,
0, NULL);
}
errno_t async_data_read_forward_3_0(async_exch_t *exch, sysarg_t imethod,
sysarg_t arg1, sysarg_t arg2, sysarg_t arg3)
{
return async_data_read_forward_fast(exch, imethod, arg1, arg2, arg3,
0, NULL);
}
errno_t async_data_read_forward_4_0(async_exch_t *exch, sysarg_t imethod,
sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
{
return async_data_read_forward_fast(exch, imethod, arg1, arg2, arg3,
arg4, NULL);
}
errno_t async_data_read_forward_0_1(async_exch_t *exch, sysarg_t imethod,
ipc_call_t *dataptr)
{
return async_data_read_forward_fast(exch, imethod, 0, 0, 0,
0, dataptr);
}
errno_t async_data_read_forward_1_1(async_exch_t *exch, sysarg_t imethod,
sysarg_t arg1, ipc_call_t *dataptr)
{
return async_data_read_forward_fast(exch, imethod, arg1, 0, 0,
0, dataptr);
}
errno_t async_data_read_forward_2_1(async_exch_t *exch, sysarg_t imethod,
sysarg_t arg1, sysarg_t arg2, ipc_call_t *dataptr)
{
return async_data_read_forward_fast(exch, imethod, arg1, arg2, 0,
0, dataptr);
}
errno_t async_data_read_forward_3_1(async_exch_t *exch, sysarg_t imethod,
sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, ipc_call_t *dataptr)
{
return async_data_read_forward_fast(exch, imethod, arg1, arg2, arg3,
0, dataptr);
}
errno_t async_data_read_forward_4_1(async_exch_t *exch, sysarg_t imethod,
sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
ipc_call_t *dataptr)
{
return async_data_read_forward_fast(exch, imethod, arg1, arg2, arg3,
arg4, dataptr);
}
bool async_data_write_receive(ipc_call_t *call, size_t *size)
{
assert(call);
async_get_call(call);
if (ipc_get_imethod(call) != IPC_M_DATA_WRITE)
return false;
if (size)
*size = (size_t) ipc_get_arg2(call);
return true;
}
errno_t async_data_write_finalize(ipc_call_t *call, void *dst, size_t size)
{
assert(call);
return async_answer_2(call, EOK, (sysarg_t) dst, (sysarg_t) size);
}
errno_t async_data_write_accept(void **data, const bool nullterm,
const size_t min_size, const size_t max_size, const size_t granularity,
size_t *received)
{
assert(data);
ipc_call_t call;
size_t size;
if (!async_data_write_receive(&call, &size)) {
async_answer_0(&call, EINVAL);
return EINVAL;
}
if (size < min_size) {
async_answer_0(&call, EINVAL);
return EINVAL;
}
if ((max_size > 0) && (size > max_size)) {
async_answer_0(&call, EINVAL);
return EINVAL;
}
if ((granularity > 0) && ((size % granularity) != 0)) {
async_answer_0(&call, EINVAL);
return EINVAL;
}
void *arg_data;
if (nullterm)
arg_data = malloc(size + 1);
else
arg_data = malloc(size);
if (arg_data == NULL) {
async_answer_0(&call, ENOMEM);
return ENOMEM;
}
errno_t rc = async_data_write_finalize(&call, arg_data, size);
if (rc != EOK) {
free(arg_data);
return rc;
}
if (nullterm)
((char *) arg_data)[size] = 0;
*data = arg_data;
if (received != NULL)
*received = size;
return EOK;
}
void async_data_write_void(errno_t retval)
{
ipc_call_t call;
async_data_write_receive(&call, NULL);
async_answer_0(&call, retval);
}
static errno_t async_data_write_forward_fast(async_exch_t *exch,
sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, sysarg_t arg3,
sysarg_t arg4, ipc_call_t *dataptr)
{
if (exch == NULL)
return ENOENT;
ipc_call_t call;
if (!async_data_write_receive(&call, NULL)) {
async_answer_0(&call, EINVAL);
return EINVAL;
}
aid_t msg = async_send_4(exch, imethod, arg1, arg2, arg3, arg4,
dataptr);
if (msg == 0) {
async_answer_0(&call, EINVAL);
return EINVAL;
}
errno_t retval = ipc_forward_fast(call.cap_handle, exch->phone, 0, 0, 0,
IPC_FF_ROUTE_FROM_ME);
if (retval != EOK) {
async_forget(msg);
async_answer_0(&call, retval);
return retval;
}
errno_t rc;
async_wait_for(msg, &rc);
return (errno_t) rc;
}
errno_t async_data_write_forward_0_0(async_exch_t *exch, sysarg_t imethod)
{
return async_data_write_forward_fast(exch, imethod, 0, 0, 0,
0, NULL);
}
errno_t async_data_write_forward_1_0(async_exch_t *exch, sysarg_t imethod,
sysarg_t arg1)
{
return async_data_write_forward_fast(exch, imethod, arg1, 0, 0,
0, NULL);
}
errno_t async_data_write_forward_2_0(async_exch_t *exch, sysarg_t imethod,
sysarg_t arg1, sysarg_t arg2)
{
return async_data_write_forward_fast(exch, imethod, arg1, arg2, 0,
0, NULL);
}
errno_t async_data_write_forward_3_0(async_exch_t *exch, sysarg_t imethod,
sysarg_t arg1, sysarg_t arg2, sysarg_t arg3)
{
return async_data_write_forward_fast(exch, imethod, arg1, arg2, arg3,
0, NULL);
}
errno_t async_data_write_forward_4_0(async_exch_t *exch, sysarg_t imethod,
sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
{
return async_data_write_forward_fast(exch, imethod, arg1, arg2, arg3,
arg4, NULL);
}
errno_t async_data_write_forward_0_1(async_exch_t *exch, sysarg_t imethod,
ipc_call_t *dataptr)
{
return async_data_write_forward_fast(exch, imethod, 0, 0, 0,
0, dataptr);
}
errno_t async_data_write_forward_1_1(async_exch_t *exch, sysarg_t imethod,
sysarg_t arg1, ipc_call_t *dataptr)
{
return async_data_write_forward_fast(exch, imethod, arg1, 0, 0,
0, dataptr);
}
errno_t async_data_write_forward_2_1(async_exch_t *exch, sysarg_t imethod,
sysarg_t arg1, sysarg_t arg2, ipc_call_t *dataptr)
{
return async_data_write_forward_fast(exch, imethod, arg1, arg2, 0,
0, dataptr);
}
errno_t async_data_write_forward_3_1(async_exch_t *exch, sysarg_t imethod,
sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, ipc_call_t *dataptr)
{
return async_data_write_forward_fast(exch, imethod, arg1, arg2, arg3,
0, dataptr);
}
errno_t async_data_write_forward_4_1(async_exch_t *exch, sysarg_t imethod,
sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4,
ipc_call_t *dataptr)
{
return async_data_write_forward_fast(exch, imethod, arg1, arg2, arg3,
arg4, dataptr);
}
async_sess_t *async_callback_receive(exch_mgmt_t mgmt)
{
ipc_call_t call;
async_get_call(&call);
cap_phone_handle_t phandle = (cap_handle_t) ipc_get_arg5(&call);
if ((ipc_get_imethod(&call) != IPC_M_CONNECT_TO_ME) ||
!cap_handle_valid((phandle))) {
async_answer_0(&call, EINVAL);
return NULL;
}
async_sess_t *sess = calloc(1, sizeof(async_sess_t));
if (sess == NULL) {
async_answer_0(&call, ENOMEM);
return NULL;
}
sess->iface = 0;
sess->mgmt = mgmt;
sess->phone = phandle;
fibril_mutex_initialize(&sess->remote_state_mtx);
list_initialize(&sess->exch_list);
fibril_mutex_initialize(&sess->mutex);
async_answer_0(&call, EOK);
return sess;
}
async_sess_t *async_callback_receive_start(exch_mgmt_t mgmt, ipc_call_t *call)
{
cap_phone_handle_t phandle = (cap_handle_t) ipc_get_arg5(call);
if ((ipc_get_imethod(call) != IPC_M_CONNECT_TO_ME) ||
!cap_handle_valid((phandle)))
return NULL;
async_sess_t *sess = calloc(1, sizeof(async_sess_t));
if (sess == NULL)
return NULL;
sess->iface = 0;
sess->mgmt = mgmt;
sess->phone = phandle;
fibril_mutex_initialize(&sess->remote_state_mtx);
list_initialize(&sess->exch_list);
fibril_mutex_initialize(&sess->mutex);
return sess;
}
bool async_state_change_receive(ipc_call_t *call)
{
assert(call);
async_get_call(call);
if (ipc_get_imethod(call) != IPC_M_STATE_CHANGE_AUTHORIZE)
return false;
return true;
}
errno_t async_state_change_finalize(ipc_call_t *call, async_exch_t *other_exch)
{
assert(call);
return async_answer_1(call, EOK, cap_handle_raw(other_exch->phone));
}
__noreturn void async_manager(void)
{
fibril_event_t ever = FIBRIL_EVENT_INIT;
fibril_wait_for(&ever);
__builtin_unreachable();
}
HelenOS homepage, sources at GitHub