Index: uspace/lib/c/generic/inet/udp.c
===================================================================
--- uspace/lib/c/generic/inet/udp.c	(revision fab2746a5541d35bc79cbed917f66153231e4a4b)
+++ uspace/lib/c/generic/inet/udp.c	(revision fab2746a5541d35bc79cbed917f66153231e4a4b)
@@ -0,0 +1,394 @@
+/*
+ * Copyright (c) 2015 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/** @file UDP API
+ */
+
+#include <errno.h>
+#include <inet/endpoint.h>
+#include <inet/udp.h>
+#include <io/log.h>
+#include <ipc/services.h>
+#include <ipc/udp.h>
+#include <loc.h>
+#include <stdlib.h>
+
+#include <stdio.h>
+
+static void udp_cb_conn(ipc_callid_t, ipc_call_t *, void *);
+
+static int udp_callback_create(udp_t *udp)
+{
+	async_exch_t *exch = async_exchange_begin(udp->sess);
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "udp_callback_create()\n");
+
+	aid_t req = async_send_0(exch, UDP_CALLBACK_CREATE, NULL);
+	int rc = async_connect_to_me(exch, 0, 0, 0, udp_cb_conn, udp);
+	async_exchange_end(exch);
+
+	if (rc != EOK)
+		return rc;
+
+	sysarg_t retval;
+	async_wait_for(req, &retval);
+
+	return retval;
+}
+
+int udp_create(udp_t **rudp)
+{
+	udp_t *udp;
+	service_id_t udp_svcid;
+	int rc;
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "udp_create()\n");
+
+	udp = calloc(1, sizeof(udp_t));
+	if (udp == NULL) {
+		rc = ENOMEM;
+		goto error;
+	}
+
+	list_initialize(&udp->assoc);
+
+	rc = loc_service_get_id(SERVICE_NAME_UDP, &udp_svcid,
+	    IPC_FLAG_BLOCKING);
+	if (rc != EOK) {
+		rc = EIO;
+		goto error;
+	}
+
+	udp->sess = loc_service_connect(EXCHANGE_SERIALIZE, udp_svcid,
+	    IPC_FLAG_BLOCKING);
+	if (udp->sess == NULL) {
+		rc = EIO;
+		goto error;
+	}
+
+	rc = udp_callback_create(udp);
+	if (rc != EOK) {
+		rc = EIO;
+		goto error;
+	}
+
+	*rudp = udp;
+	return EOK;
+error:
+	free(udp);
+	return rc;
+}
+
+void udp_destroy(udp_t *udp)
+{
+	if (udp == NULL)
+		return;
+
+	async_hangup(udp->sess);
+	free(udp);
+}
+
+int udp_assoc_create(udp_t *udp, inet_ep2_t *ep2, udp_cb_t *cb, void *arg,
+    udp_assoc_t **rassoc)
+{
+	async_exch_t *exch;
+	udp_assoc_t *assoc;
+	ipc_call_t answer;
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "udp_assoc_create()\n");
+
+	assoc = calloc(1, sizeof(udp_assoc_t));
+	if (assoc == NULL)
+		return ENOMEM;
+
+	exch = async_exchange_begin(udp->sess);
+	aid_t req = async_send_0(exch, UDP_ASSOC_CREATE, &answer);
+	sysarg_t rc = async_data_write_start(exch, (void *)ep2,
+	    sizeof(inet_ep2_t));
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		sysarg_t rc_orig;
+		async_wait_for(req, &rc_orig);
+		if (rc_orig != EOK)
+			rc = rc_orig;
+		goto error;
+	}
+
+	async_wait_for(req, &rc);
+	if (rc != EOK)
+		goto error;
+
+	assoc->udp = udp;
+	assoc->id = IPC_GET_ARG1(answer);
+	assoc->cb = cb;
+	assoc->cb_arg = arg;
+
+	list_append(&assoc->ludp, &udp->assoc);
+	*rassoc = assoc;
+
+	return EOK;
+error:
+	free(assoc);
+	return (int) rc;
+}
+
+void udp_assoc_destroy(udp_assoc_t *assoc)
+{
+	async_exch_t *exch;
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "udp_assoc_destroy()\n");
+
+	if (assoc == NULL)
+		return;
+
+	list_remove(&assoc->ludp);
+
+	exch = async_exchange_begin(assoc->udp->sess);
+	sysarg_t rc = async_req_1_0(exch, UDP_ASSOC_DESTROY, assoc->id);
+	async_exchange_end(exch);
+
+	free(assoc);
+	(void) rc;
+}
+
+int udp_assoc_send_msg(udp_assoc_t *assoc, inet_ep_t *dest, void *data,
+    size_t bytes)
+{
+	async_exch_t *exch;
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "udp_assoc_send_msg()\n");
+
+	exch = async_exchange_begin(assoc->udp->sess);
+	aid_t req = async_send_1(exch, UDP_ASSOC_SEND_MSG, assoc->id, NULL);
+
+	sysarg_t rc = async_data_write_start(exch, (void *)dest,
+	    sizeof(inet_ep_t));
+	if (rc != EOK) {
+		async_exchange_end(exch);
+		async_forget(req);
+		return rc;
+	}
+
+	rc = async_data_write_start(exch, data, bytes);
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	async_wait_for(req, &rc);
+	return rc;
+}
+
+void *udp_assoc_userptr(udp_assoc_t *assoc)
+{
+	return assoc->cb_arg;
+}
+
+size_t udp_rmsg_size(udp_rmsg_t *rmsg)
+{
+	return rmsg->size;
+}
+
+int udp_rmsg_read(udp_rmsg_t *rmsg, size_t off, void *buf, size_t bsize)
+{
+	async_exch_t *exch;
+	ipc_call_t answer;
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "udp_rmsg_read()");
+
+	exch = async_exchange_begin(rmsg->udp->sess);
+	aid_t req = async_send_1(exch, UDP_RMSG_READ, off, &answer);
+	int rc = async_data_read_start(exch, buf, bsize);
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_NOTE, "udp_rmsg_read() - rc != EOK");
+		async_forget(req);
+		return rc;
+	}
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "udp_rmsg_read() - wait for req");
+	sysarg_t retval;
+	async_wait_for(req, &retval);
+	if (retval != EOK) {
+		log_msg(LOG_DEFAULT, LVL_NOTE, "udp_rmsg_read() - retval != EOK");
+		return retval;
+	}
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "udp_rmsg_read() - OK");
+	return EOK;
+}
+
+void udp_rmsg_remote_ep(udp_rmsg_t *rmsg, inet_ep_t *ep)
+{
+	*ep = rmsg->remote_ep;
+}
+
+uint8_t udp_rerr_type(udp_rerr_t *rerr)
+{
+	return 0;
+}
+
+uint8_t udp_rerr_code(udp_rerr_t *rerr)
+{
+	return 0;
+}
+
+static int udp_rmsg_info(udp_t *udp, udp_rmsg_t *rmsg)
+{
+	async_exch_t *exch;
+	inet_ep_t ep;
+	ipc_call_t answer;
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "udp_rmsg_info()\n");
+
+	exch = async_exchange_begin(udp->sess);
+	aid_t req = async_send_0(exch, UDP_RMSG_INFO, &answer);
+	int rc = async_data_read_start(exch, &ep, sizeof(inet_ep_t));
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	sysarg_t retval;
+	async_wait_for(req, &retval);
+	if (retval != EOK)
+		return retval;
+
+	rmsg->udp = udp;
+	rmsg->assoc_id = IPC_GET_ARG1(answer);
+	rmsg->size = IPC_GET_ARG2(answer);
+	rmsg->remote_ep = ep;
+	return EOK;
+}
+
+static int udp_rmsg_discard(udp_t *udp)
+{
+	async_exch_t *exch;
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "udp_rmsg_discard()\n");
+
+	exch = async_exchange_begin(udp->sess);
+	sysarg_t rc = async_req_0_0(exch, UDP_RMSG_DISCARD);
+	async_exchange_end(exch);
+
+	return rc;
+}
+
+static int udp_assoc_get(udp_t *udp, sysarg_t id, udp_assoc_t **rassoc)
+{
+	list_foreach(udp->assoc, ludp, udp_assoc_t, assoc) {
+		if (assoc->id == id) {
+			*rassoc = assoc;
+			return EOK;
+		}
+	}
+
+	return EINVAL;
+}
+
+static void udp_ev_data(udp_t *udp, ipc_callid_t iid, ipc_call_t *icall)
+{
+	udp_rmsg_t rmsg;
+	udp_assoc_t *assoc;
+	int rc;
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "udp_ev_data()\n");
+
+	while (true) {
+		rc = udp_rmsg_info(udp, &rmsg);
+		if (rc != EOK) {
+			log_msg(LOG_DEFAULT, LVL_NOTE, "Error getting message info\n");
+			break;
+		}
+
+		rc = udp_assoc_get(udp, rmsg.assoc_id, &assoc);
+		if (rc != EOK) {
+			log_msg(LOG_DEFAULT, LVL_NOTE, "assoc ID %zu not found\n",
+			    rmsg.assoc_id);
+			continue;
+		}
+
+		if (assoc->cb != NULL && assoc->cb->recv_msg != NULL)
+			assoc->cb->recv_msg(assoc, &rmsg);
+
+		rc = udp_rmsg_discard(udp);
+		if (rc != EOK) {
+			log_msg(LOG_DEFAULT, LVL_NOTE, "Error discarding message\n");
+			break;
+		}
+	}
+
+	async_answer_0(iid, EOK);
+}
+
+static void udp_cb_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	udp_t *udp = (udp_t *)arg;
+
+	async_answer_0(iid, EOK);
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "udp_cb_conn()\n");
+
+	while (true) {
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+
+		log_msg(LOG_DEFAULT, LVL_NOTE, "udp_cb_conn() - msg %d\n",
+		    (int)IPC_GET_IMETHOD(call));
+		if (!IPC_GET_IMETHOD(call)) {
+			/* TODO: Handle hangup */
+			return;
+		}
+
+		switch (IPC_GET_IMETHOD(call)) {
+		case UDP_EV_DATA:
+			udp_ev_data(udp, callid, &call);
+			break;
+		default:
+			async_answer_0(callid, ENOTSUP);
+			break;
+		}
+	}
+}
+
+/** @}
+ */
