Index: uspace/lib/device/src/devman.c
===================================================================
--- uspace/lib/device/src/devman.c	(revision 5fc82448bbd01f2950b893989095ed4c1b1c99b3)
+++ uspace/lib/device/src/devman.c	(revision 5fc82448bbd01f2950b893989095ed4c1b1c99b3)
@@ -0,0 +1,734 @@
+/*
+ * Copyright (c) 2007 Josef Cejka
+ * Copyright (c) 2013 Jiri Svoboda
+ * Copyright (c) 2010 Lenka Trochtova
+ * 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
+ */
+
+#include <adt/list.h>
+#include <str.h>
+#include <ipc/services.h>
+#include <ns.h>
+#include <ipc/devman.h>
+#include <devman.h>
+#include <fibril_synch.h>
+#include <async.h>
+#include <errno.h>
+#include <stdlib.h>
+
+static FIBRIL_MUTEX_INITIALIZE(devman_driver_block_mutex);
+static FIBRIL_MUTEX_INITIALIZE(devman_client_block_mutex);
+
+static FIBRIL_MUTEX_INITIALIZE(devman_driver_mutex);
+static FIBRIL_MUTEX_INITIALIZE(devman_client_mutex);
+
+static async_sess_t *devman_driver_block_sess = NULL;
+static async_sess_t *devman_client_block_sess = NULL;
+
+static async_sess_t *devman_driver_sess = NULL;
+static async_sess_t *devman_client_sess = NULL;
+
+static void clone_session(fibril_mutex_t *mtx, async_sess_t *src,
+    async_sess_t **dst)
+{
+	fibril_mutex_lock(mtx);
+
+	if ((*dst == NULL) && (src != NULL))
+		*dst = src;
+
+	fibril_mutex_unlock(mtx);
+}
+
+/** Start an async exchange on the devman session (blocking).
+ *
+ * @param iface Device manager interface to choose
+ *
+ * @return New exchange.
+ *
+ */
+async_exch_t *devman_exchange_begin_blocking(iface_t iface)
+{
+	switch (iface) {
+	case INTERFACE_DDF_DRIVER:
+		fibril_mutex_lock(&devman_driver_block_mutex);
+
+		while (devman_driver_block_sess == NULL) {
+			clone_session(&devman_driver_mutex, devman_driver_sess,
+			    &devman_driver_block_sess);
+
+			if (devman_driver_block_sess == NULL)
+				devman_driver_block_sess =
+				    service_connect_blocking(SERVICE_DEVMAN,
+				    INTERFACE_DDF_DRIVER, 0, NULL);
+		}
+
+		fibril_mutex_unlock(&devman_driver_block_mutex);
+
+		clone_session(&devman_driver_mutex, devman_driver_block_sess,
+		    &devman_driver_sess);
+
+		return async_exchange_begin(devman_driver_block_sess);
+	case INTERFACE_DDF_CLIENT:
+		fibril_mutex_lock(&devman_client_block_mutex);
+
+		while (devman_client_block_sess == NULL) {
+			clone_session(&devman_client_mutex, devman_client_sess,
+			    &devman_client_block_sess);
+
+			if (devman_client_block_sess == NULL)
+				devman_client_block_sess =
+				    service_connect_blocking(SERVICE_DEVMAN,
+				    INTERFACE_DDF_CLIENT, 0, NULL);
+		}
+
+		fibril_mutex_unlock(&devman_client_block_mutex);
+
+		clone_session(&devman_client_mutex, devman_client_block_sess,
+		    &devman_client_sess);
+
+		return async_exchange_begin(devman_client_block_sess);
+	default:
+		return NULL;
+	}
+}
+
+/** Start an async exchange on the devman session.
+ *
+ * @param iface Device manager interface to choose
+ *
+ * @return New exchange.
+ *
+ */
+async_exch_t *devman_exchange_begin(iface_t iface)
+{
+	switch (iface) {
+	case INTERFACE_DDF_DRIVER:
+		fibril_mutex_lock(&devman_driver_mutex);
+
+		if (devman_driver_sess == NULL)
+			devman_driver_sess =
+			    service_connect(SERVICE_DEVMAN,
+			    INTERFACE_DDF_DRIVER, 0, NULL);
+
+		fibril_mutex_unlock(&devman_driver_mutex);
+
+		if (devman_driver_sess == NULL)
+			return NULL;
+
+		return async_exchange_begin(devman_driver_sess);
+	case INTERFACE_DDF_CLIENT:
+		fibril_mutex_lock(&devman_client_mutex);
+
+		if (devman_client_sess == NULL)
+			devman_client_sess =
+			    service_connect(SERVICE_DEVMAN,
+			    INTERFACE_DDF_CLIENT, 0, NULL);
+
+		fibril_mutex_unlock(&devman_client_mutex);
+
+		if (devman_client_sess == NULL)
+			return NULL;
+
+		return async_exchange_begin(devman_client_sess);
+	default:
+		return NULL;
+	}
+}
+
+/** Finish an async exchange on the devman session.
+ *
+ * @param exch Exchange to be finished.
+ *
+ */
+void devman_exchange_end(async_exch_t *exch)
+{
+	async_exchange_end(exch);
+}
+
+/** Register running driver with device manager. */
+errno_t devman_driver_register(const char *name)
+{
+	async_exch_t *exch = devman_exchange_begin_blocking(INTERFACE_DDF_DRIVER);
+
+	ipc_call_t answer;
+	aid_t req = async_send_2(exch, DEVMAN_DRIVER_REGISTER, 0, 0, &answer);
+	errno_t retval = async_data_write_start(exch, name, str_size(name));
+
+	devman_exchange_end(exch);
+
+	if (retval != EOK) {
+		async_forget(req);
+		return retval;
+	}
+
+	exch = devman_exchange_begin(INTERFACE_DDF_DRIVER);
+	async_connect_to_me(exch, INTERFACE_ANY, 0, 0);
+	devman_exchange_end(exch);
+
+	async_wait_for(req, &retval);
+	return retval;
+}
+
+/** Add function to a device.
+ *
+ * Request devman to add a new function to the specified device owned by
+ * this driver task.
+ *
+ * @param name      Name of the new function
+ * @param ftype     Function type, fun_inner or fun_exposed
+ * @param match_ids Match IDs (should be empty for fun_exposed)
+ * @param devh      Devman handle of the device
+ * @param funh      Place to store handle of the new function
+ *
+ * @return EOK on success or an error code.
+ *
+ */
+errno_t devman_add_function(const char *name, fun_type_t ftype,
+    match_id_list_t *match_ids, devman_handle_t devh, devman_handle_t *funh)
+{
+	unsigned long match_count = list_count(&match_ids->ids);
+	async_exch_t *exch = devman_exchange_begin_blocking(INTERFACE_DDF_DRIVER);
+
+	ipc_call_t answer;
+	aid_t req = async_send_3(exch, DEVMAN_ADD_FUNCTION, (sysarg_t) ftype,
+	    devh, match_count, &answer);
+	errno_t retval = async_data_write_start(exch, name, str_size(name));
+	if (retval != EOK) {
+		devman_exchange_end(exch);
+		async_forget(req);
+		return retval;
+	}
+
+	list_foreach(match_ids->ids, link, match_id_t, match_id) {
+		ipc_call_t answer2;
+		aid_t req2 = async_send_1(exch, DEVMAN_ADD_MATCH_ID,
+		    match_id->score, &answer2);
+		retval = async_data_write_start(exch, match_id->id,
+		    str_size(match_id->id));
+		if (retval != EOK) {
+			devman_exchange_end(exch);
+			async_forget(req2);
+			async_forget(req);
+			return retval;
+		}
+
+		async_wait_for(req2, &retval);
+		if (retval != EOK) {
+			devman_exchange_end(exch);
+			async_forget(req);
+			return retval;
+		}
+	}
+
+	devman_exchange_end(exch);
+
+	async_wait_for(req, &retval);
+	if (retval == EOK) {
+		if (funh != NULL)
+			*funh = (int) ipc_get_arg1(&answer);
+	} else {
+		if (funh != NULL)
+			*funh = -1;
+	}
+
+	return retval;
+}
+
+errno_t devman_add_device_to_category(devman_handle_t devman_handle,
+    const char *cat_name)
+{
+	async_exch_t *exch = devman_exchange_begin_blocking(INTERFACE_DDF_DRIVER);
+
+	ipc_call_t answer;
+	aid_t req = async_send_1(exch, DEVMAN_ADD_DEVICE_TO_CATEGORY,
+	    devman_handle, &answer);
+	errno_t retval = async_data_write_start(exch, cat_name,
+	    str_size(cat_name));
+
+	devman_exchange_end(exch);
+
+	if (retval != EOK) {
+		async_forget(req);
+		return retval;
+	}
+
+	async_wait_for(req, &retval);
+	return retval;
+}
+
+async_sess_t *devman_device_connect(devman_handle_t handle, unsigned int flags)
+{
+	async_sess_t *sess;
+
+	if (flags & IPC_FLAG_BLOCKING)
+		sess = service_connect_blocking(SERVICE_DEVMAN,
+		    INTERFACE_DEVMAN_DEVICE, handle, NULL);
+	else
+		sess = service_connect(SERVICE_DEVMAN,
+		    INTERFACE_DEVMAN_DEVICE, handle, NULL);
+
+	return sess;
+}
+
+/** Remove function from device.
+ *
+ * Request devman to remove function owned by this driver task.
+ * @param funh      Devman handle of the function
+ *
+ * @return EOK on success or an error code.
+ */
+errno_t devman_remove_function(devman_handle_t funh)
+{
+	async_exch_t *exch;
+	errno_t retval;
+
+	exch = devman_exchange_begin_blocking(INTERFACE_DDF_DRIVER);
+	retval = async_req_1_0(exch, DEVMAN_REMOVE_FUNCTION, (sysarg_t) funh);
+	devman_exchange_end(exch);
+
+	return retval;
+}
+
+errno_t devman_drv_fun_online(devman_handle_t funh)
+{
+	async_exch_t *exch = devman_exchange_begin(INTERFACE_DDF_DRIVER);
+	if (exch == NULL)
+		return ENOMEM;
+
+	errno_t retval = async_req_1_0(exch, DEVMAN_DRV_FUN_ONLINE, funh);
+
+	devman_exchange_end(exch);
+	return retval;
+}
+
+errno_t devman_drv_fun_offline(devman_handle_t funh)
+{
+	async_exch_t *exch = devman_exchange_begin(INTERFACE_DDF_DRIVER);
+	if (exch == NULL)
+		return ENOMEM;
+
+	errno_t retval = async_req_1_0(exch, DEVMAN_DRV_FUN_OFFLINE, funh);
+
+	devman_exchange_end(exch);
+	return retval;
+}
+
+async_sess_t *devman_parent_device_connect(devman_handle_t handle,
+    unsigned int flags)
+{
+	async_sess_t *sess;
+
+	if (flags & IPC_FLAG_BLOCKING)
+		sess = service_connect_blocking(SERVICE_DEVMAN,
+		    INTERFACE_DEVMAN_PARENT, handle, NULL);
+	else
+		sess = service_connect(SERVICE_DEVMAN,
+		    INTERFACE_DEVMAN_PARENT, handle, NULL);
+
+	return sess;
+}
+
+errno_t devman_fun_get_handle(const char *pathname, devman_handle_t *handle,
+    unsigned int flags)
+{
+	async_exch_t *exch;
+
+	if (flags & IPC_FLAG_BLOCKING)
+		exch = devman_exchange_begin_blocking(INTERFACE_DDF_CLIENT);
+	else {
+		exch = devman_exchange_begin(INTERFACE_DDF_CLIENT);
+		if (exch == NULL)
+			return ENOMEM;
+	}
+
+	ipc_call_t answer;
+	aid_t req = async_send_2(exch, DEVMAN_DEVICE_GET_HANDLE, flags, 0,
+	    &answer);
+	errno_t retval = async_data_write_start(exch, pathname,
+	    str_size(pathname));
+
+	devman_exchange_end(exch);
+
+	if (retval != EOK) {
+		async_forget(req);
+		return retval;
+	}
+
+	async_wait_for(req, &retval);
+
+	if (retval != EOK) {
+		if (handle != NULL)
+			*handle = (devman_handle_t) -1;
+
+		return retval;
+	}
+
+	if (handle != NULL)
+		*handle = (devman_handle_t) ipc_get_arg1(&answer);
+
+	return retval;
+}
+
+static errno_t devman_get_str_internal(sysarg_t method, sysarg_t arg1,
+    sysarg_t arg2, sysarg_t *r1, char *buf, size_t buf_size)
+{
+	async_exch_t *exch;
+	ipc_call_t dreply;
+	size_t act_size;
+	errno_t dretval;
+
+	exch = devman_exchange_begin_blocking(INTERFACE_DDF_CLIENT);
+
+	ipc_call_t answer;
+	aid_t req = async_send_2(exch, method, arg1, arg2, &answer);
+	aid_t dreq = async_data_read(exch, buf, buf_size - 1, &dreply);
+	async_wait_for(dreq, &dretval);
+
+	devman_exchange_end(exch);
+
+	if (dretval != EOK) {
+		async_forget(req);
+		return dretval;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	if (retval != EOK) {
+		return retval;
+	}
+
+	if (r1 != NULL)
+		*r1 = ipc_get_arg1(&answer);
+	act_size = ipc_get_arg2(&dreply);
+	assert(act_size <= buf_size - 1);
+	buf[act_size] = '\0';
+
+	return EOK;
+}
+
+errno_t devman_fun_get_path(devman_handle_t handle, char *buf, size_t buf_size)
+{
+	return devman_get_str_internal(DEVMAN_FUN_GET_PATH, handle, 0, NULL,
+	    buf, buf_size);
+}
+
+errno_t devman_fun_get_match_id(devman_handle_t handle, size_t index, char *buf,
+    size_t buf_size, unsigned int *rscore)
+{
+	errno_t rc;
+	sysarg_t score = 0;
+
+	rc = devman_get_str_internal(DEVMAN_FUN_GET_MATCH_ID, handle, index,
+	    &score, buf, buf_size);
+	if (rc != EOK)
+		return rc;
+
+	*rscore = score;
+	return rc;
+}
+
+errno_t devman_fun_get_name(devman_handle_t handle, char *buf, size_t buf_size)
+{
+	return devman_get_str_internal(DEVMAN_FUN_GET_NAME, handle, 0, NULL,
+	    buf, buf_size);
+}
+
+errno_t devman_fun_get_driver_name(devman_handle_t handle, char *buf, size_t buf_size)
+{
+	return devman_get_str_internal(DEVMAN_FUN_GET_DRIVER_NAME, handle, 0,
+	    NULL, buf, buf_size);
+}
+
+errno_t devman_fun_online(devman_handle_t funh)
+{
+	async_exch_t *exch = devman_exchange_begin(INTERFACE_DDF_CLIENT);
+	if (exch == NULL)
+		return ENOMEM;
+
+	errno_t retval = async_req_1_0(exch, DEVMAN_FUN_ONLINE, funh);
+
+	devman_exchange_end(exch);
+	return retval;
+}
+
+errno_t devman_fun_offline(devman_handle_t funh)
+{
+	async_exch_t *exch = devman_exchange_begin(INTERFACE_DDF_CLIENT);
+	if (exch == NULL)
+		return ENOMEM;
+
+	errno_t retval = async_req_1_0(exch, DEVMAN_FUN_OFFLINE, funh);
+
+	devman_exchange_end(exch);
+	return retval;
+}
+
+static errno_t devman_get_handles_once(sysarg_t method, sysarg_t arg1,
+    devman_handle_t *handle_buf, size_t buf_size, size_t *act_size)
+{
+	async_exch_t *exch = devman_exchange_begin_blocking(INTERFACE_DDF_CLIENT);
+
+	ipc_call_t answer;
+	aid_t req = async_send_1(exch, method, arg1, &answer);
+	errno_t rc = async_data_read_start(exch, handle_buf, buf_size);
+
+	devman_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+
+	errno_t retval;
+	async_wait_for(req, &retval);
+
+	if (retval != EOK) {
+		return retval;
+	}
+
+	*act_size = ipc_get_arg1(&answer);
+	return EOK;
+}
+
+/** Get list of handles.
+ *
+ * Returns an allocated array of handles.
+ *
+ * @param method	IPC method
+ * @param arg1		IPC argument 1
+ * @param data		Place to store pointer to array of handles
+ * @param count		Place to store number of handles
+ * @return 		EOK on success or an error code
+ */
+static errno_t devman_get_handles_internal(sysarg_t method, sysarg_t arg1,
+    devman_handle_t **data, size_t *count)
+{
+	devman_handle_t *handles;
+	size_t act_size;
+	size_t alloc_size;
+	errno_t rc;
+
+	*data = NULL;
+	act_size = 0;	/* silence warning */
+
+	rc = devman_get_handles_once(method, arg1, NULL, 0,
+	    &act_size);
+	if (rc != EOK)
+		return rc;
+
+	alloc_size = act_size;
+	handles = malloc(alloc_size);
+	if (handles == NULL)
+		return ENOMEM;
+
+	while (true) {
+		rc = devman_get_handles_once(method, arg1, handles, alloc_size,
+		    &act_size);
+		if (rc != EOK)
+			return rc;
+
+		if (act_size <= alloc_size)
+			break;
+
+		alloc_size *= 2;
+		free(handles);
+
+		handles = malloc(alloc_size);
+		if (handles == NULL)
+			return ENOMEM;
+	}
+
+	*count = act_size / sizeof(devman_handle_t);
+	*data = handles;
+	return EOK;
+}
+
+errno_t devman_fun_get_child(devman_handle_t funh, devman_handle_t *devh)
+{
+	async_exch_t *exch = devman_exchange_begin(INTERFACE_DDF_CLIENT);
+	if (exch == NULL)
+		return ENOMEM;
+
+	errno_t retval = async_req_1_1(exch, DEVMAN_FUN_GET_CHILD,
+	    funh, devh);
+
+	devman_exchange_end(exch);
+	return retval;
+}
+
+errno_t devman_dev_get_functions(devman_handle_t devh, devman_handle_t **funcs,
+    size_t *count)
+{
+	return devman_get_handles_internal(DEVMAN_DEV_GET_FUNCTIONS,
+	    devh, funcs, count);
+}
+
+errno_t devman_dev_get_parent(devman_handle_t devh, devman_handle_t *funh)
+{
+	async_exch_t *exch = devman_exchange_begin(INTERFACE_DDF_CLIENT);
+	if (exch == NULL)
+		return ENOMEM;
+
+	errno_t retval = async_req_1_1(exch, DEVMAN_DEV_GET_PARENT,
+	    devh, funh);
+
+	devman_exchange_end(exch);
+	return retval;
+}
+
+errno_t devman_fun_sid_to_handle(service_id_t sid, devman_handle_t *handle)
+{
+	async_exch_t *exch = devman_exchange_begin(INTERFACE_DDF_CLIENT);
+	if (exch == NULL)
+		return ENOMEM;
+
+	errno_t retval = async_req_1_1(exch, DEVMAN_FUN_SID_TO_HANDLE,
+	    sid, handle);
+
+	devman_exchange_end(exch);
+	return retval;
+}
+
+errno_t devman_get_drivers(devman_handle_t **drvs,
+    size_t *count)
+{
+	return devman_get_handles_internal(DEVMAN_GET_DRIVERS, 0, drvs, count);
+}
+
+errno_t devman_driver_get_devices(devman_handle_t drvh, devman_handle_t **devs,
+    size_t *count)
+{
+	return devman_get_handles_internal(DEVMAN_DRIVER_GET_DEVICES,
+	    drvh, devs, count);
+}
+
+errno_t devman_driver_get_handle(const char *drvname, devman_handle_t *handle)
+{
+	async_exch_t *exch;
+
+	exch = devman_exchange_begin(INTERFACE_DDF_CLIENT);
+	if (exch == NULL)
+		return ENOMEM;
+
+	ipc_call_t answer;
+	aid_t req = async_send_0(exch, DEVMAN_DRIVER_GET_HANDLE, &answer);
+	errno_t retval = async_data_write_start(exch, drvname,
+	    str_size(drvname));
+
+	devman_exchange_end(exch);
+
+	if (retval != EOK) {
+		async_forget(req);
+		return retval;
+	}
+
+	async_wait_for(req, &retval);
+
+	if (retval != EOK) {
+		if (handle != NULL)
+			*handle = (devman_handle_t) -1;
+
+		return retval;
+	}
+
+	if (handle != NULL)
+		*handle = (devman_handle_t) ipc_get_arg1(&answer);
+
+	return retval;
+}
+
+errno_t devman_driver_get_match_id(devman_handle_t handle, size_t index, char *buf,
+    size_t buf_size, unsigned int *rscore)
+{
+	errno_t rc;
+	sysarg_t score = 0;
+
+	rc = devman_get_str_internal(DEVMAN_DRIVER_GET_MATCH_ID, handle, index,
+	    &score, buf, buf_size);
+	if (rc != EOK)
+		return rc;
+
+	*rscore = score;
+	return rc;
+}
+
+errno_t devman_driver_get_name(devman_handle_t handle, char *buf, size_t buf_size)
+{
+	return devman_get_str_internal(DEVMAN_DRIVER_GET_NAME, handle, 0, NULL,
+	    buf, buf_size);
+}
+
+errno_t devman_driver_get_state(devman_handle_t drvh, driver_state_t *rstate)
+{
+	sysarg_t state;
+	async_exch_t *exch = devman_exchange_begin(INTERFACE_DDF_CLIENT);
+	if (exch == NULL)
+		return ENOMEM;
+
+	errno_t rc = async_req_1_1(exch, DEVMAN_DRIVER_GET_STATE, drvh,
+	    &state);
+
+	devman_exchange_end(exch);
+	if (rc != EOK)
+		return rc;
+
+	*rstate = state;
+	return rc;
+}
+
+errno_t devman_driver_load(devman_handle_t drvh)
+{
+	async_exch_t *exch = devman_exchange_begin(INTERFACE_DDF_CLIENT);
+	if (exch == NULL)
+		return ENOMEM;
+
+	errno_t rc = async_req_1_0(exch, DEVMAN_DRIVER_LOAD, drvh);
+
+	devman_exchange_end(exch);
+	return rc;
+}
+
+errno_t devman_driver_unload(devman_handle_t drvh)
+{
+	async_exch_t *exch = devman_exchange_begin(INTERFACE_DDF_CLIENT);
+	if (exch == NULL)
+		return ENOMEM;
+
+	errno_t rc = async_req_1_0(exch, DEVMAN_DRIVER_UNLOAD, drvh);
+
+	devman_exchange_end(exch);
+	return rc;
+}
+
+/** @}
+ */
