Index: uspace/srv/devman/devman.c
===================================================================
--- uspace/srv/devman/devman.c	(revision dc6b1488002a77ead533c7f3333fec8d6503cc42)
+++ uspace/srv/devman/devman.c	(revision c5be39b48e3de00d342394cb29ba1f563fc45dbd)
@@ -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 dc6b1488002a77ead533c7f3333fec8d6503cc42)
+++ uspace/srv/devman/devman.h	(revision c5be39b48e3de00d342394cb29ba1f563fc45dbd)
@@ -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 dc6b1488002a77ead533c7f3333fec8d6503cc42)
+++ uspace/srv/devman/main.c	(revision c5be39b48e3de00d342394cb29ba1f563fc45dbd)
@@ -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:
