Index: uspace/app/devctl/devctl.c
===================================================================
--- uspace/app/devctl/devctl.c	(revision e64df9a72bbbfdbe211ee53a9c2961e91eed0659)
+++ uspace/app/devctl/devctl.c	(revision 1a5b2521ea58a25aa1f13d0850fa06ae93fba965)
@@ -37,4 +37,5 @@
 #include <stdio.h>
 #include <stdlib.h>
+#include <str_error.h>
 #include <sys/typefmt.h>
 
@@ -43,5 +44,5 @@
 #define MAX_NAME_LENGTH 1024
 
-static int fun_tree_print(devman_handle_t funh, int lvl)
+static int fun_subtree_print(devman_handle_t funh, int lvl)
 {
 	char name[MAX_NAME_LENGTH];
@@ -84,5 +85,5 @@
 
 	for (i = 0; i < count; i++)
-		fun_tree_print(cfuns[i], lvl + 1);
+		fun_subtree_print(cfuns[i], lvl + 1);
 
 	free(cfuns);
@@ -90,5 +91,5 @@
 }
 
-int main(int argc, char *argv[])
+static int fun_tree_print(void)
 {
 	devman_handle_t root_fun;
@@ -98,10 +99,96 @@
 	if (rc != EOK) {
 		printf(NAME ": Error resolving root function.\n");
+		return EIO;
+	}
+
+	rc = fun_subtree_print(root_fun, 0);
+	if (rc != EOK)
+		return EIO;
+
+	return EOK;
+}
+
+static int fun_online(const char *path)
+{
+	devman_handle_t funh;
+	int rc;
+
+	rc = devman_fun_get_handle(path, &funh, 0);
+	if (rc != EOK) {
+		printf(NAME ": Error resolving device function '%s' (%s)\n",
+		    path, str_error(rc));
+		return rc;
+	}
+
+	rc = devman_fun_online(funh);
+	if (rc != EOK) {
+		printf(NAME ": Failed to online function '%s'.\n", path);
+		return rc;
+	}
+
+	return EOK;
+}
+
+static int fun_offline(const char *path)
+{
+	devman_handle_t funh;
+	int rc;
+
+	rc = devman_fun_get_handle(path, &funh, 0);
+	if (rc != EOK) {
+		printf(NAME ": Error resolving device function '%s' (%s)\n",
+		    path, str_error(rc));
+		return rc;
+	}
+
+	rc = devman_fun_offline(funh);
+	if (rc != EOK) {
+		printf(NAME ": Failed to offline function '%s'.\n", path);
+		return rc;
+	}
+
+	return EOK;
+}
+
+static void print_syntax(void)
+{
+	printf("syntax: devctl [(online|offline) <function>]\n");
+}
+
+int main(int argc, char *argv[])
+{
+	int rc;
+
+	if (argc == 1) {
+		rc = fun_tree_print();
+		if (rc != EOK)
+			return 2;
+	} else if (str_cmp(argv[1], "online") == 0) {
+		if (argc < 3) {
+			printf(NAME ": Argument missing.\n");
+			print_syntax();
+			return 1;
+		}
+
+		rc = fun_online(argv[2]);
+		if (rc != EOK) {
+			return 2;
+		}
+	} else if (str_cmp(argv[1], "offline") == 0) {
+		if (argc < 3) {
+			printf(NAME ": Argument missing.\n");
+			print_syntax();
+			return 1;
+		}
+
+		rc = fun_offline(argv[2]);
+		if (rc != EOK) {
+			return 2;
+		}
+	} else {
+		printf(NAME ": Invalid argument '%s'.\n", argv[1]);
+		print_syntax();
 		return 1;
 	}
-
-	rc = fun_tree_print(root_fun, 0);
-	if (rc != EOK)
-		return 1;
 
 	return 0;
Index: uspace/drv/infrastructure/rootvirt/rootvirt.c
===================================================================
--- uspace/drv/infrastructure/rootvirt/rootvirt.c	(revision e64df9a72bbbfdbe211ee53a9c2961e91eed0659)
+++ uspace/drv/infrastructure/rootvirt/rootvirt.c	(revision 1a5b2521ea58a25aa1f13d0850fa06ae93fba965)
@@ -63,7 +63,11 @@
 
 static int rootvirt_add_device(ddf_dev_t *dev);
+static int rootvirt_fun_online(ddf_fun_t *fun);
+static int rootvirt_fun_offline(ddf_fun_t *fun);
 
 static driver_ops_t rootvirt_ops = {
-	.add_device = &rootvirt_add_device
+	.add_device = &rootvirt_add_device,
+	.fun_online = &rootvirt_fun_online,
+	.fun_offline = &rootvirt_fun_offline
 };
 
@@ -140,4 +144,16 @@
 }
 
+static int rootvirt_fun_online(ddf_fun_t *fun)
+{
+	ddf_msg(LVL_DEBUG, "rootvirt_fun_online()");
+	return ddf_fun_online(fun);
+}
+
+static int rootvirt_fun_offline(ddf_fun_t *fun)
+{
+	ddf_msg(LVL_DEBUG, "rootvirt_fun_offline()");
+	return ddf_fun_offline(fun);
+}
+
 int main(int argc, char *argv[])
 {
Index: uspace/drv/test/test1/test1.c
===================================================================
--- uspace/drv/test/test1/test1.c	(revision e64df9a72bbbfdbe211ee53a9c2961e91eed0659)
+++ uspace/drv/test/test1/test1.c	(revision 1a5b2521ea58a25aa1f13d0850fa06ae93fba965)
@@ -1,4 +1,5 @@
 /*
  * Copyright (c) 2010 Vojtech Horky
+ * Copyright (c) 2011 Jiri Svoboda
  * All rights reserved.
  *
@@ -40,7 +41,13 @@
 
 static int test1_add_device(ddf_dev_t *dev);
+static int test1_dev_remove(ddf_dev_t *dev);
+static int test1_fun_online(ddf_fun_t *fun);
+static int test1_fun_offline(ddf_fun_t *fun);
 
 static driver_ops_t driver_ops = {
-	.add_device = &test1_add_device
+	.add_device = &test1_add_device,
+	.dev_remove = &test1_dev_remove,
+	.fun_online = &test1_fun_online,
+	.fun_offline = &test1_fun_offline
 };
 
@@ -49,4 +56,10 @@
 	.driver_ops = &driver_ops
 };
+
+typedef struct {
+	ddf_fun_t *fun_a;
+	ddf_fun_t *clone;
+	ddf_fun_t *child;
+} test1_t;
 
 /** Register child and inform user about it.
@@ -60,5 +73,5 @@
 static int register_fun_verbose(ddf_dev_t *parent, const char *message,
     const char *name, const char *match_id, int match_score,
-    int expected_rc)
+    int expected_rc, ddf_fun_t **pfun)
 {
 	ddf_fun_t *fun = NULL;
@@ -103,4 +116,7 @@
 	}
 
+	if (pfun != NULL)
+		*pfun = fun;
+
 	return rc;
 }
@@ -126,8 +142,15 @@
 {
 	ddf_fun_t *fun_a;
+	test1_t *test1;
 	int rc;
 
 	ddf_msg(LVL_DEBUG, "add_device(name=\"%s\", handle=%d)",
 	    dev->name, (int) dev->handle);
+
+	test1 = calloc(1, sizeof(test1_t));
+	if (test1 == NULL) {
+		ddf_msg(LVL_ERROR, "Failed allocating softstate.\n");
+		return ENOMEM;
+	}
 
 	fun_a = ddf_fun_create(dev, fun_exposed, "a");
@@ -140,4 +163,5 @@
 	if (rc != EOK) {
 		ddf_msg(LVL_ERROR, "Failed binding function 'a'.");
+		ddf_fun_destroy(fun_a);
 		return rc;
 	}
@@ -151,17 +175,80 @@
 		(void) register_fun_verbose(dev,
 		    "cloning myself ;-)", "clone",
-		    "virtual&test1", 10, EOK);
+		    "virtual&test1", 10, EOK, &test1->clone);
 		(void) register_fun_verbose(dev,
 		    "cloning myself twice ;-)", "clone",
-		    "virtual&test1", 10, EEXISTS);
+		    "virtual&test1", 10, EEXISTS, NULL);
 	} else if (str_cmp(dev->name, "clone") == 0) {
 		(void) register_fun_verbose(dev,
 		    "run by the same task", "child",
-		    "virtual&test1&child", 10, EOK);
+		    "virtual&test1&child", 10, EOK, &test1->child);
 	}
 
 	ddf_msg(LVL_DEBUG, "Device `%s' accepted.", dev->name);
 
+	test1->fun_a = fun_a;
+	dev->driver_data = test1;
 	return EOK;
+}
+
+static int fun_remove(ddf_fun_t *fun, const char *name)
+{
+	int rc;
+
+	ddf_msg(LVL_DEBUG, "fun_remove(%p, '%s')\n", fun, name);
+	rc = ddf_fun_offline(fun);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Error offlining function '%s'.", name);
+		return rc;
+	}
+
+	rc = ddf_fun_unbind(fun);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Failed offlining function '%s'.", name);
+		return rc;
+	}
+
+	ddf_fun_destroy(fun);
+	return EOK;
+}
+
+static int test1_dev_remove(ddf_dev_t *dev)
+{
+	test1_t *test1 = (test1_t *)dev->driver_data;
+	int rc;
+
+	ddf_msg(LVL_DEBUG, "test1_dev_remove(%p)", dev);
+
+	if (test1->fun_a != NULL) {
+		rc = fun_remove(test1->fun_a, "a");
+		if (rc != EOK)
+			return rc;
+	}
+
+	if (test1->clone != NULL) {
+		rc = fun_remove(test1->clone, "clone");
+		if (rc != EOK)
+			return rc;
+	}
+
+	if (test1->child != NULL) {
+		rc = fun_remove(test1->child, "child");
+		if (rc != EOK)
+			return rc;
+	}
+
+	return EOK;
+}
+
+static int test1_fun_online(ddf_fun_t *fun)
+{
+	ddf_msg(LVL_DEBUG, "test1_fun_online()");
+	return ddf_fun_online(fun);
+}
+
+static int test1_fun_offline(ddf_fun_t *fun)
+{
+	ddf_msg(LVL_DEBUG, "test1_fun_offline()");
+	return ddf_fun_offline(fun);
 }
 
Index: uspace/lib/c/generic/devman.c
===================================================================
--- uspace/lib/c/generic/devman.c	(revision e64df9a72bbbfdbe211ee53a9c2961e91eed0659)
+++ uspace/lib/c/generic/devman.c	(revision 1a5b2521ea58a25aa1f13d0850fa06ae93fba965)
@@ -327,4 +327,28 @@
 }
 
+int devman_drv_fun_online(devman_handle_t funh)
+{
+	async_exch_t *exch = devman_exchange_begin(DEVMAN_DRIVER);
+	if (exch == NULL)
+		return ENOMEM;
+	
+	sysarg_t retval = async_req_1_0(exch, DEVMAN_DRV_FUN_ONLINE, funh);
+	
+	devman_exchange_end(exch);
+	return (int) retval;
+}
+
+int devman_drv_fun_offline(devman_handle_t funh)
+{
+	async_exch_t *exch = devman_exchange_begin(DEVMAN_DRIVER);
+	if (exch == NULL)
+		return ENOMEM;
+	
+	sysarg_t retval = async_req_1_0(exch, DEVMAN_DRV_FUN_OFFLINE, funh);
+	
+	devman_exchange_end(exch);
+	return (int) retval;
+}
+
 async_sess_t *devman_parent_device_connect(exch_mgmt_t mgmt,
     devman_handle_t handle, unsigned int flags)
@@ -430,4 +454,28 @@
 }
 
+int devman_fun_online(devman_handle_t funh)
+{
+	async_exch_t *exch = devman_exchange_begin(DEVMAN_CLIENT);
+	if (exch == NULL)
+		return ENOMEM;
+	
+	sysarg_t retval = async_req_1_0(exch, DEVMAN_FUN_ONLINE, funh);
+	
+	devman_exchange_end(exch);
+	return (int) retval;
+}
+
+int devman_fun_offline(devman_handle_t funh)
+{
+	async_exch_t *exch = devman_exchange_begin(DEVMAN_CLIENT);
+	if (exch == NULL)
+		return ENOMEM;
+	
+	sysarg_t retval = async_req_1_0(exch, DEVMAN_FUN_OFFLINE, funh);
+	
+	devman_exchange_end(exch);
+	return (int) retval;
+}
+
 static int devman_get_handles_once(sysarg_t method, sysarg_t arg1,
     devman_handle_t *handle_buf, size_t buf_size, size_t *act_size)
Index: uspace/lib/c/include/devman.h
===================================================================
--- uspace/lib/c/include/devman.h	(revision e64df9a72bbbfdbe211ee53a9c2961e91eed0659)
+++ uspace/lib/c/include/devman.h	(revision 1a5b2521ea58a25aa1f13d0850fa06ae93fba965)
@@ -50,4 +50,6 @@
     devman_handle_t, devman_handle_t *);
 extern int devman_remove_function(devman_handle_t);
+extern int devman_drv_fun_online(devman_handle_t);
+extern int devman_drv_fun_offline(devman_handle_t);
 
 extern async_sess_t *devman_device_connect(exch_mgmt_t, devman_handle_t,
@@ -63,4 +65,6 @@
 extern int devman_fun_get_name(devman_handle_t, char *, size_t);
 extern int devman_fun_get_path(devman_handle_t, char *, size_t);
+extern int devman_fun_online(devman_handle_t);
+extern int devman_fun_offline(devman_handle_t);
 
 extern int devman_add_device_to_category(devman_handle_t, const char *);
Index: uspace/lib/c/include/ipc/devman.h
===================================================================
--- uspace/lib/c/include/ipc/devman.h	(revision e64df9a72bbbfdbe211ee53a9c2961e91eed0659)
+++ uspace/lib/c/include/ipc/devman.h	(revision 1a5b2521ea58a25aa1f13d0850fa06ae93fba965)
@@ -139,9 +139,14 @@
 	DEVMAN_ADD_MATCH_ID,
 	DEVMAN_ADD_DEVICE_TO_CATEGORY,
+	DEVMAN_DRV_FUN_ONLINE,
+	DEVMAN_DRV_FUN_OFFLINE,
 	DEVMAN_REMOVE_FUNCTION
 } driver_to_devman_t;
 
 typedef enum {
-	DRIVER_ADD_DEVICE = IPC_FIRST_USER_METHOD
+	DRIVER_DEV_ADD = IPC_FIRST_USER_METHOD,
+	DRIVER_DEV_REMOVE,
+	DRIVER_FUN_ONLINE,
+	DRIVER_FUN_OFFLINE,
 
 } devman_to_driver_t;
@@ -152,4 +157,6 @@
 	DEVMAN_FUN_GET_CHILD,
 	DEVMAN_FUN_GET_NAME,
+	DEVMAN_FUN_ONLINE,
+	DEVMAN_FUN_OFFLINE,
 	DEVMAN_FUN_GET_PATH,
 	DEVMAN_FUN_SID_TO_HANDLE
Index: uspace/lib/drv/generic/driver.c
===================================================================
--- uspace/lib/drv/generic/driver.c	(revision e64df9a72bbbfdbe211ee53a9c2961e91eed0659)
+++ uspace/lib/drv/generic/driver.c	(revision 1a5b2521ea58a25aa1f13d0850fa06ae93fba965)
@@ -63,4 +63,8 @@
 
 /** Devices */
+LIST_INITIALIZE(devices);
+FIBRIL_MUTEX_INITIALIZE(devices_mutex);
+
+/** Functions */
 LIST_INITIALIZE(functions);
 FIBRIL_MUTEX_INITIALIZE(functions_mutex);
@@ -227,19 +231,30 @@
 }
 
-static ddf_fun_t *driver_get_function(list_t *functions, devman_handle_t handle)
+static ddf_dev_t *driver_get_device(devman_handle_t handle)
+{
+	ddf_dev_t *dev = NULL;
+	
+	assert(fibril_mutex_is_locked(&devices_mutex));
+	
+	list_foreach(devices, link) {
+		dev = list_get_instance(link, ddf_dev_t, link);
+		if (dev->handle == handle)
+			return dev;
+	}
+	
+	return NULL;
+}
+
+static ddf_fun_t *driver_get_function(devman_handle_t handle)
 {
 	ddf_fun_t *fun = NULL;
 	
-	fibril_mutex_lock(&functions_mutex);
-	
-	list_foreach(*functions, link) {
+	assert(fibril_mutex_is_locked(&functions_mutex));
+	
+	list_foreach(functions, link) {
 		fun = list_get_instance(link, ddf_fun_t, link);
-		if (fun->handle == handle) {
-			fibril_mutex_unlock(&functions_mutex);
+		if (fun->handle == handle)
 			return fun;
-		}
-	}
-	
-	fibril_mutex_unlock(&functions_mutex);
+	}
 	
 	return NULL;
@@ -270,5 +285,86 @@
 		delete_device(dev);
 	
+	fibril_mutex_lock(&devices_mutex);
+	list_append(&dev->link, &devices);
+	fibril_mutex_unlock(&devices_mutex);
+	
 	async_answer_0(iid, res);
+}
+
+static void driver_dev_remove(ipc_callid_t iid, ipc_call_t *icall)
+{
+	devman_handle_t devh;
+	ddf_dev_t *dev;
+	int rc;
+	
+	printf("libdrv: driver_dev_offline()\n");
+	devh = IPC_GET_ARG1(*icall);
+	
+	fibril_mutex_lock(&devices_mutex);
+	dev = driver_get_device(devh);
+	fibril_mutex_unlock(&devices_mutex);
+	/* XXX need lock on dev */
+	
+	if (dev == NULL) {
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	if (driver->driver_ops->dev_remove != NULL)
+		rc = driver->driver_ops->dev_remove(dev);
+	else
+		rc = ENOTSUP;
+	
+	async_answer_0(iid, (sysarg_t) rc);
+}
+
+static void driver_fun_online(ipc_callid_t iid, ipc_call_t *icall)
+{
+	devman_handle_t funh;
+	ddf_fun_t *fun;
+	int rc;
+	
+	funh = IPC_GET_ARG1(*icall);
+	fibril_mutex_lock(&functions_mutex);
+	fun = driver_get_function(funh);
+	fibril_mutex_unlock(&functions_mutex);
+	/* XXX Need lock on fun */
+	
+	if (fun == NULL) {
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	if (driver->driver_ops->fun_online != NULL)
+		rc = driver->driver_ops->fun_online(fun);
+	else
+		rc = ENOTSUP;
+	
+	async_answer_0(iid, (sysarg_t) rc);
+}
+
+static void driver_fun_offline(ipc_callid_t iid, ipc_call_t *icall)
+{
+	devman_handle_t funh;
+	ddf_fun_t *fun;
+	int rc;
+	
+	funh = IPC_GET_ARG1(*icall);
+	fibril_mutex_lock(&functions_mutex);
+	fun = driver_get_function(funh);
+	fibril_mutex_unlock(&functions_mutex);
+	/* XXX Need lock on fun */
+	
+	if (fun == NULL) {
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	if (driver->driver_ops->fun_offline != NULL)
+		rc = driver->driver_ops->fun_offline(fun);
+	else
+		rc = ENOTSUP;
+	
+	async_answer_0(iid, (sysarg_t) rc);
 }
 
@@ -286,9 +382,18 @@
 		
 		switch (IPC_GET_IMETHOD(call)) {
-		case DRIVER_ADD_DEVICE:
+		case DRIVER_DEV_ADD:
 			driver_add_device(callid, &call);
 			break;
+		case DRIVER_DEV_REMOVE:
+			driver_dev_remove(callid, &call);
+			break;
+		case DRIVER_FUN_ONLINE:
+			driver_fun_online(callid, &call);
+			break;
+		case DRIVER_FUN_OFFLINE:
+			driver_fun_offline(callid, &call);
+			break;
 		default:
-			async_answer_0(callid, ENOENT);
+			async_answer_0(callid, ENOTSUP);
 		}
 	}
@@ -308,5 +413,9 @@
 	 */
 	devman_handle_t handle = IPC_GET_ARG2(*icall);
-	ddf_fun_t *fun = driver_get_function(&functions, handle);
+
+	fibril_mutex_lock(&functions_mutex);
+	ddf_fun_t *fun = driver_get_function(handle);
+	fibril_mutex_unlock(&functions_mutex);
+	/* XXX Need a lock on fun */
 	
 	if (fun == NULL) {
@@ -614,5 +723,5 @@
  * the function invisible to the system.
  *
- * @param fun		Function to bind
+ * @param fun		Function to unbind
  * @return		EOK on success or negative error code
  */
@@ -623,5 +732,4 @@
 	assert(fun->bound == true);
 	
-	add_to_functions_list(fun);
 	res = devman_remove_function(fun->handle);
 	if (res != EOK)
@@ -631,4 +739,40 @@
 	
 	fun->bound = false;
+	return EOK;
+}
+
+/** Online function.
+ *
+ * @param fun		Function to online
+ * @return		EOK on success or negative error code
+ */
+int ddf_fun_online(ddf_fun_t *fun)
+{
+	int res;
+	
+	assert(fun->bound == true);
+	
+	res = devman_drv_fun_online(fun->handle);
+	if (res != EOK)
+		return res;
+	
+	return EOK;
+}
+
+/** Offline function.
+ *
+ * @param fun		Function to offline
+ * @return		EOK on success or negative error code
+ */
+int ddf_fun_offline(ddf_fun_t *fun)
+{
+	int res;
+	
+	assert(fun->bound == true);
+	
+	res = devman_drv_fun_offline(fun->handle);
+	if (res != EOK)
+		return res;
+	
 	return EOK;
 }
Index: uspace/lib/drv/include/ddf/driver.h
===================================================================
--- uspace/lib/drv/include/ddf/driver.h	(revision e64df9a72bbbfdbe211ee53a9c2961e91eed0659)
+++ uspace/lib/drv/include/ddf/driver.h	(revision 1a5b2521ea58a25aa1f13d0850fa06ae93fba965)
@@ -132,6 +132,11 @@
 typedef struct driver_ops {
 	/** Callback method for passing a new device to the device driver */
-	int (*add_device)(ddf_dev_t *dev);
-	/* TODO: add other generic driver operations */
+	int (*add_device)(ddf_dev_t *);
+	/** Ask driver to remove a device */
+	int (*dev_remove)(ddf_dev_t *);
+	/** Ask driver to online a specific function */
+	int (*fun_online)(ddf_fun_t *);
+	/** Ask driver to offline a specific function */
+	int (*fun_offline)(ddf_fun_t *);
 } driver_ops_t;
 
@@ -150,4 +155,6 @@
 extern int ddf_fun_bind(ddf_fun_t *);
 extern int ddf_fun_unbind(ddf_fun_t *);
+extern int ddf_fun_online(ddf_fun_t *);
+extern int ddf_fun_offline(ddf_fun_t *);
 extern int ddf_fun_add_match_id(ddf_fun_t *, const char *, int);
 
Index: uspace/srv/devman/devman.c
===================================================================
--- uspace/srv/devman/devman.c	(revision e64df9a72bbbfdbe211ee53a9c2961e91eed0659)
+++ uspace/srv/devman/devman.c	(revision 1a5b2521ea58a25aa1f13d0850fa06ae93fba965)
@@ -483,4 +483,26 @@
 }
 
+/** Detach driver from device.
+ *
+ * @param node		The device's node in the device tree.
+ * @param drv		The driver.
+ */
+void detach_driver(dev_node_t *dev)
+{
+	/* XXX need lock on dev */
+	driver_t *drv = dev->drv;
+	
+	assert(drv != NULL);
+	log_msg(LVL_DEBUG, "detach_driver(dev=\"%s\",drv=\"%s\")",
+	    dev->pfun->pathname, drv->name);
+	
+	fibril_mutex_lock(&drv->driver_mutex);
+	
+	dev->drv = NULL;
+	list_remove(&dev->driver_devices);
+	
+	fibril_mutex_unlock(&drv->driver_mutex);
+}
+
 /** Start a driver
  *
@@ -726,5 +748,5 @@
 	
 	ipc_call_t answer;
-	aid_t req = async_send_2(exch, DRIVER_ADD_DEVICE, dev->handle,
+	aid_t req = async_send_2(exch, DRIVER_DEV_ADD, dev->handle,
 	    parent_handle, &answer);
 	
@@ -800,4 +822,65 @@
 }
 
+int driver_dev_remove(dev_node_t *dev)
+{
+	async_exch_t *exch;
+	sysarg_t retval;
+	driver_t *drv;
+	
+	assert(dev != NULL);
+	log_msg(LVL_DEBUG, "driver_dev_remove(%p)", dev);
+	drv = dev->drv;
+	
+	exch = async_exchange_begin(drv->sess);
+	retval = async_req_1_0(exch, DRIVER_DEV_REMOVE, dev->handle);
+	async_exchange_end(exch);
+	
+	return retval;
+
+}
+
+int driver_fun_online(fun_node_t *fun)
+{
+	async_exch_t *exch;
+	sysarg_t retval;
+	driver_t *drv;
+	
+	log_msg(LVL_DEBUG, "driver_fun_online(%p)", fun);
+	if (fun->dev == NULL) {
+		/* XXX root function? */
+		return EINVAL;
+	}
+	
+	drv = fun->dev->drv;
+	
+	exch = async_exchange_begin(drv->sess);
+	retval = async_req_1_0(exch, DRIVER_FUN_ONLINE, fun->handle);
+	loc_exchange_end(exch);
+	
+	return retval;
+}
+
+int driver_fun_offline(fun_node_t *fun)
+{
+	async_exch_t *exch;
+	sysarg_t retval;
+	driver_t *drv;
+	
+	log_msg(LVL_DEBUG, "driver_fun_offline(%p)", fun);
+	if (fun->dev == NULL) {
+		/* XXX root function? */
+		return EINVAL;
+	}
+	
+	drv = fun->dev->drv;
+	
+	exch = async_exchange_begin(drv->sess);
+	retval = async_req_1_0(exch, DRIVER_FUN_OFFLINE, fun->handle);
+	loc_exchange_end(exch);
+	
+	return retval;
+
+}
+
 /** Initialize the device tree.
  *
@@ -1065,4 +1148,27 @@
 }
 
+/** Remove device from device tree.
+ *
+ * @param tree		Device tree
+ * @param dev		Device node
+ */
+void remove_dev_node(dev_tree_t *tree, dev_node_t *dev)
+{
+	assert(tree != NULL);
+	assert(dev != NULL);
+	assert(fibril_rwlock_is_write_locked(&tree->rwlock));
+	
+	log_msg(LVL_DEBUG, "remove_dev_node(dev=%p)", dev);
+	
+	/* Remove node from the handle-to-node map. */
+	unsigned long key = dev->handle;
+	hash_table_remove(&tree->devman_devices, &key, 1);
+	
+	/* Unlink from parent function. */
+	dev->pfun->child = NULL;
+	dev->pfun = NULL;
+}
+
+
 /** Insert new function into device tree.
  *
@@ -1127,4 +1233,6 @@
 	if (fun->dev != NULL)
 		list_remove(&fun->dev_functions);
+	
+	fun->dev = NULL;
 }
 
Index: uspace/srv/devman/devman.h
===================================================================
--- uspace/srv/devman/devman.h	(revision e64df9a72bbbfdbe211ee53a9c2961e91eed0659)
+++ uspace/srv/devman/devman.h	(revision 1a5b2521ea58a25aa1f13d0850fa06ae93fba965)
@@ -240,6 +240,10 @@
 extern void add_driver(driver_list_t *, driver_t *);
 extern void attach_driver(dev_node_t *, driver_t *);
+extern void detach_driver(dev_node_t *);
 extern void add_device(driver_t *, dev_node_t *, dev_tree_t *);
 extern bool start_driver(driver_t *);
+extern int driver_dev_remove(dev_node_t *);
+extern int driver_fun_online(fun_node_t *);
+extern int driver_fun_offline(fun_node_t *);
 
 extern driver_t *find_driver(driver_list_t *, const char *);
@@ -274,4 +278,5 @@
 extern bool create_root_nodes(dev_tree_t *);
 extern bool insert_dev_node(dev_tree_t *, dev_node_t *, fun_node_t *);
+extern void remove_dev_node(dev_tree_t *, dev_node_t *);
 extern bool insert_fun_node(dev_tree_t *, fun_node_t *, char *, dev_node_t *);
 extern void remove_fun_node(dev_tree_t *, fun_node_t *);
Index: uspace/srv/devman/main.c
===================================================================
--- uspace/srv/devman/main.c	(revision e64df9a72bbbfdbe211ee53a9c2961e91eed0659)
+++ uspace/srv/devman/main.c	(revision 1a5b2521ea58a25aa1f13d0850fa06ae93fba965)
@@ -237,85 +237,29 @@
 }
 
-/** Handle function registration.
- *
- * Child devices are registered by their parent's device driver.
- */
-static void devman_add_function(ipc_callid_t callid, ipc_call_t *call)
-{
-	fun_type_t ftype = (fun_type_t) IPC_GET_ARG1(*call);
-	devman_handle_t dev_handle = IPC_GET_ARG2(*call);
-	sysarg_t match_count = IPC_GET_ARG3(*call);
-	dev_tree_t *tree = &device_tree;
-	
-	fibril_rwlock_write_lock(&tree->rwlock);
-
-	dev_node_t *dev = NULL;
-	dev_node_t *pdev = find_dev_node_no_lock(&device_tree, dev_handle);
-	
-	if (pdev == NULL) {
-		fibril_rwlock_write_unlock(&tree->rwlock);
-		async_answer_0(callid, ENOENT);
-		return;
-	}
-	
-	if (ftype != fun_inner && ftype != fun_exposed) {
-		/* Unknown function type */
-		log_msg(LVL_ERROR, 
-		    "Unknown function type %d provided by driver.",
-		    (int) ftype);
-
-		fibril_rwlock_write_unlock(&tree->rwlock);
-		async_answer_0(callid, EINVAL);
-		return;
-	}
-	
-	char *fun_name = NULL;
-	int rc = async_data_write_accept((void **)&fun_name, true, 0, 0, 0, 0);
-	if (rc != EOK) {
-		fibril_rwlock_write_unlock(&tree->rwlock);
-		async_answer_0(callid, rc);
-		return;
-	}
-	
-	/* Check that function with same name is not there already. */
-	if (find_fun_node_in_device(pdev, fun_name) != NULL) {
-		fibril_rwlock_write_unlock(&tree->rwlock);
-		async_answer_0(callid, EEXISTS);
-		printf(NAME ": Warning, driver tried to register `%s' twice.\n",
-		    fun_name);
-		free(fun_name);
-		return;
-	}
-	
-	fun_node_t *fun = create_fun_node();
-	fun->ftype = ftype;
-	
-	if (!insert_fun_node(&device_tree, fun, fun_name, pdev)) {
-		fibril_rwlock_write_unlock(&tree->rwlock);
-		delete_fun_node(fun);
-		async_answer_0(callid, ENOMEM);
-		return;
-	}
-
-	if (ftype == fun_inner) {
+static int online_function(fun_node_t *fun)
+{
+	dev_node_t *dev;
+	
+	fibril_rwlock_write_lock(&device_tree.rwlock);
+	
+	if (fun->ftype == fun_inner) {
 		dev = create_dev_node();
 		if (dev == NULL) {
-			fibril_rwlock_write_unlock(&tree->rwlock);
+			fibril_rwlock_write_unlock(&device_tree.rwlock);
 			delete_fun_node(fun);
-			async_answer_0(callid, ENOMEM);
-			return;
+			return ENOMEM;
 		}
 
-		insert_dev_node(tree, dev, fun);
-	}
-
-	fibril_rwlock_write_unlock(&tree->rwlock);
+		insert_dev_node(&device_tree, dev, fun);
+	}
+	
+	fibril_rwlock_write_unlock(&device_tree.rwlock);
 	
 	log_msg(LVL_DEBUG, "devman_add_function(fun=\"%s\")", fun->pathname);
 	
-	devman_receive_match_ids(match_count, &fun->match_ids);
-
-	if (ftype == fun_inner) {
+	if (fun->ftype == fun_inner) {
+		dev = fun->child;
 		assert(dev != NULL);
+		
 		/*
 		 * Try to find a suitable driver and assign it to the device.  We do
@@ -336,5 +280,113 @@
 		}
 	} else {
-		loc_register_tree_function(fun, tree);
+		loc_register_tree_function(fun, &device_tree);
+	}
+	
+	return EOK;
+}
+
+static int offline_function(fun_node_t *fun)
+{
+	int rc;
+	
+	if (fun->ftype == fun_inner) {
+		printf("devman_drv_fun_offline(): %p is inner fun, removing "
+		    "child dev.\n", fun);
+		if (fun->child != NULL) {
+			dev_node_t *dev = fun->child;
+			
+			rc = driver_dev_remove(dev);
+			if (rc != EOK) {
+				return ENOTSUP;
+			}
+			detach_driver(dev);
+			fibril_rwlock_write_lock(&device_tree.rwlock);
+			remove_dev_node(&device_tree, dev);
+			fibril_rwlock_write_unlock(&device_tree.rwlock);
+			delete_dev_node(dev);
+		}
+	} else {
+		/* Unregister from location service */
+		rc = loc_service_unregister(fun->service_id);
+		if (rc != EOK) {
+			log_msg(LVL_ERROR, "Failed unregistering tree service.");
+			return EIO;
+		}
+		
+		fun->service_id = 0;
+	}
+	
+	return EOK;
+}
+
+/** Handle function registration.
+ *
+ * Child devices are registered by their parent's device driver.
+ */
+static void devman_add_function(ipc_callid_t callid, ipc_call_t *call)
+{
+	fun_type_t ftype = (fun_type_t) IPC_GET_ARG1(*call);
+	devman_handle_t dev_handle = IPC_GET_ARG2(*call);
+	sysarg_t match_count = IPC_GET_ARG3(*call);
+	dev_tree_t *tree = &device_tree;
+	
+	fibril_rwlock_write_lock(&tree->rwlock);
+
+	dev_node_t *pdev = find_dev_node_no_lock(&device_tree, dev_handle);
+	
+	if (pdev == NULL) {
+		fibril_rwlock_write_unlock(&tree->rwlock);
+		async_answer_0(callid, ENOENT);
+		return;
+	}
+	
+	if (ftype != fun_inner && ftype != fun_exposed) {
+		/* Unknown function type */
+		log_msg(LVL_ERROR, 
+		    "Unknown function type %d provided by driver.",
+		    (int) ftype);
+
+		fibril_rwlock_write_unlock(&tree->rwlock);
+		async_answer_0(callid, EINVAL);
+		return;
+	}
+	
+	char *fun_name = NULL;
+	int rc = async_data_write_accept((void **)&fun_name, true, 0, 0, 0, 0);
+	if (rc != EOK) {
+		fibril_rwlock_write_unlock(&tree->rwlock);
+		async_answer_0(callid, rc);
+		return;
+	}
+	
+	/* Check that function with same name is not there already. */
+	if (find_fun_node_in_device(pdev, fun_name) != NULL) {
+		fibril_rwlock_write_unlock(&tree->rwlock);
+		async_answer_0(callid, EEXISTS);
+		printf(NAME ": Warning, driver tried to register `%s' twice.\n",
+		    fun_name);
+		free(fun_name);
+		return;
+	}
+	
+	fun_node_t *fun = create_fun_node();
+	fun->ftype = ftype;
+	
+	if (!insert_fun_node(&device_tree, fun, fun_name, pdev)) {
+		fibril_rwlock_write_unlock(&tree->rwlock);
+		delete_fun_node(fun);
+		async_answer_0(callid, ENOMEM);
+		return;
+	}
+	
+	fibril_rwlock_write_unlock(&tree->rwlock);
+	
+	devman_receive_match_ids(match_count, &fun->match_ids);
+	
+	rc = online_function(fun);
+	if (rc != EOK) {
+		/* XXX clean up */
+		async_answer_0(callid, rc);
+		return;
 	}
 	
@@ -378,4 +430,62 @@
 }
 
+/** Online function by driver request.
+ *
+ */
+static void devman_drv_fun_online(ipc_callid_t iid, ipc_call_t *icall,
+    driver_t *drv)
+{
+	fun_node_t *fun;
+	int rc;
+
+	printf("devman_drv_fun_online()\n");
+	fibril_rwlock_write_lock(&device_tree.rwlock);
+	fun = find_fun_node_no_lock(&device_tree, IPC_GET_ARG1(*icall));
+	fibril_rwlock_write_unlock(&device_tree.rwlock); /* XXX FIXME */
+	
+	if (fun == NULL || fun->dev == NULL || fun->dev->drv != drv) {
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	rc = online_function(fun);
+	if (rc != EOK) {
+		printf("devman_drv_fun_online() online_fun->ERROR\n");
+		async_answer_0(iid, (sysarg_t) rc);
+		return;
+	}
+	printf("devman_drv_fun_online() online_fun->OK\n");
+	
+	async_answer_0(iid, (sysarg_t) EOK);
+}
+
+
+/** Offline function by driver request.
+ *
+ */
+static void devman_drv_fun_offline(ipc_callid_t iid, ipc_call_t *icall,
+    driver_t *drv)
+{
+	fun_node_t *fun;
+	int rc;
+
+	fibril_rwlock_write_lock(&device_tree.rwlock);
+	fun = find_fun_node_no_lock(&device_tree, IPC_GET_ARG1(*icall));
+	fibril_rwlock_write_unlock(&device_tree.rwlock); /* XXX FIXME */
+	
+	if (fun == NULL || fun->dev == NULL || fun->dev->drv != drv) {
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	rc = offline_function(fun);
+	if (rc != EOK) {
+		async_answer_0(iid, (sysarg_t) rc);
+		return;
+	}
+	
+	async_answer_0(iid, (sysarg_t) EOK);
+}
+
 /** Remove function. */
 static void devman_remove_function(ipc_callid_t callid, ipc_call_t *call)
@@ -399,14 +509,19 @@
 		/* Handle possible descendants */
 		/* TODO */
-		log_msg(LVL_WARN, "devman_remove_function(): not handling "
-		    "descendants\n");
+		if (fun->child != NULL) {
+			log_msg(LVL_WARN, "devman_remove_function(): not handling "
+			    "descendants\n");
+		}
 	} else {
-		/* Unregister from location service */
-		rc = loc_service_unregister(fun->service_id);
-		if (rc != EOK) {
-			log_msg(LVL_ERROR, "Failed unregistering tree service.");
-			fibril_rwlock_write_unlock(&tree->rwlock);
-			async_answer_0(callid, EIO);
-			return;
+		if (fun->service_id != 0) {
+			/* Unregister from location service */
+			rc = loc_service_unregister(fun->service_id);
+			if (rc != EOK) {
+				log_msg(LVL_ERROR, "Failed unregistering tree "
+				    "service.");
+				fibril_rwlock_write_unlock(&tree->rwlock);
+				async_answer_0(callid, EIO);
+				return;
+			}
 		}
 	}
@@ -485,9 +600,15 @@
 			devman_add_function_to_cat(callid, &call);
 			break;
+		case DEVMAN_DRV_FUN_ONLINE:
+			devman_drv_fun_online(callid, &call, driver);
+			break;
+		case DEVMAN_DRV_FUN_OFFLINE:
+			devman_drv_fun_offline(callid, &call, driver);
+			break;
 		case DEVMAN_REMOVE_FUNCTION:
 			devman_remove_function(callid, &call);
 			break;
 		default:
-			async_answer_0(callid, EINVAL); 
+			async_answer_0(callid, EINVAL);
 			break;
 		}
@@ -666,4 +787,56 @@
 }
 
+/** Online function.
+ *
+ * Send a request to online a function to the responsible driver.
+ * The driver may offline other functions if necessary (i.e. if the state
+ * of this function is linked to state of another function somehow).
+ */
+static void devman_fun_online(ipc_callid_t iid, ipc_call_t *icall)
+{
+	fun_node_t *fun;
+	int rc;
+
+	fibril_rwlock_write_lock(&device_tree.rwlock);
+	fun = find_fun_node_no_lock(&device_tree, IPC_GET_ARG1(*icall));
+	fibril_rwlock_write_unlock(&device_tree.rwlock); /* XXX FIXME */
+	
+	if (fun == NULL) {
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	rc = driver_fun_online(fun);
+	
+	async_answer_0(iid, (sysarg_t) rc);
+}
+
+/** Offline function.
+ *
+ * Send a request to offline a function to the responsible driver. As
+ * a result the subtree rooted at that function should be cleanly
+ * detatched. The driver may offline other functions if necessary
+ * (i.e. if the state of this function is linked to state of another
+ * function somehow).
+ */
+static void devman_fun_offline(ipc_callid_t iid, ipc_call_t *icall)
+{
+	fun_node_t *fun;
+	int rc;
+
+	fibril_rwlock_write_lock(&device_tree.rwlock);
+	fun = find_fun_node_no_lock(&device_tree, IPC_GET_ARG1(*icall));
+	fibril_rwlock_write_unlock(&device_tree.rwlock); /* XXX FIXME */
+	
+	if (fun == NULL) {
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	rc = driver_fun_offline(fun);
+	
+	async_answer_0(iid, (sysarg_t) rc);
+}
+
 /** Find handle for the function instance identified by its service ID. */
 static void devman_fun_sid_to_handle(ipc_callid_t iid, ipc_call_t *icall)
@@ -709,4 +882,10 @@
 		case DEVMAN_FUN_GET_PATH:
 			devman_fun_get_path(callid, &call);
+			break;
+		case DEVMAN_FUN_ONLINE:
+			devman_fun_online(callid, &call);
+			break;
+		case DEVMAN_FUN_OFFLINE:
+			devman_fun_offline(callid, &call);
 			break;
 		case DEVMAN_FUN_SID_TO_HANDLE:
