Index: uspace/drv/test/test1/test1.c
===================================================================
--- uspace/drv/test/test1/test1.c	(revision 16cc9a67c9e8675b44eda89eeeb9e3971f7905f7)
+++ uspace/drv/test/test1/test1.c	(revision 80a96d2355e43d996baf70b026fae98a9d19add6)
@@ -42,4 +42,5 @@
 static int test1_add_device(ddf_dev_t *dev);
 static int test1_dev_remove(ddf_dev_t *dev);
+static int test1_dev_gone(ddf_dev_t *dev);
 static int test1_fun_online(ddf_fun_t *fun);
 static int test1_fun_offline(ddf_fun_t *fun);
@@ -48,4 +49,5 @@
 	.add_device = &test1_add_device,
 	.dev_remove = &test1_dev_remove,
+	.dev_gone = &test1_dev_gone,
 	.fun_online = &test1_fun_online,
 	.fun_offline = &test1_fun_offline
@@ -213,4 +215,19 @@
 }
 
+static int fun_unbind(ddf_fun_t *fun, const char *name)
+{
+	int rc;
+
+	ddf_msg(LVL_DEBUG, "fun_unbind(%p, '%s')", fun, name);
+	rc = ddf_fun_unbind(fun);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Failed unbinding function '%s'.", name);
+		return rc;
+	}
+
+	ddf_fun_destroy(fun);
+	return EOK;
+}
+
 static int test1_dev_remove(ddf_dev_t *dev)
 {
@@ -234,4 +251,32 @@
 	if (test1->child != NULL) {
 		rc = fun_remove(test1->child, "child");
+		if (rc != EOK)
+			return rc;
+	}
+
+	return EOK;
+}
+
+static int test1_dev_gone(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_unbind(test1->fun_a, "a");
+		if (rc != EOK)
+			return rc;
+	}
+
+	if (test1->clone != NULL) {
+		rc = fun_unbind(test1->clone, "clone");
+		if (rc != EOK)
+			return rc;
+	}
+
+	if (test1->child != NULL) {
+		rc = fun_unbind(test1->child, "child");
 		if (rc != EOK)
 			return rc;
Index: uspace/drv/test/test2/test2.c
===================================================================
--- uspace/drv/test/test2/test2.c	(revision 16cc9a67c9e8675b44eda89eeeb9e3971f7905f7)
+++ uspace/drv/test/test2/test2.c	(revision 80a96d2355e43d996baf70b026fae98a9d19add6)
@@ -42,4 +42,5 @@
 static int test2_add_device(ddf_dev_t *dev);
 static int test2_dev_remove(ddf_dev_t *dev);
+static int test2_dev_gone(ddf_dev_t *dev);
 static int test2_fun_online(ddf_fun_t *fun);
 static int test2_fun_offline(ddf_fun_t *fun);
@@ -48,4 +49,5 @@
 	.add_device = &test2_add_device,
 	.dev_remove = &test2_dev_remove,
+	.dev_gone = &test2_dev_gone,
 	.fun_online = &test2_fun_online,
 	.fun_offline = &test2_fun_offline
@@ -111,10 +113,10 @@
 }
 
-/** Add child devices after some sleep.
+/** Simulate plugging and surprise unplugging.
  *
  * @param arg Parent device structure (ddf_dev_t *).
  * @return Always EOK.
  */
-static int postponed_birth(void *arg)
+static int plug_unplug(void *arg)
 {
 	test2_t *test2 = (test2_t *) arg;
@@ -144,4 +146,12 @@
 	test2->fun_a = fun_a;
 
+	async_usleep(10000000);
+
+	ddf_msg(LVL_NOTE, "Unbinding function test1.");
+	ddf_fun_unbind(test2->test1);
+	async_usleep(1000000);
+	ddf_msg(LVL_NOTE, "Unbinding function child.");
+	ddf_fun_unbind(test2->child);
+
 	return EOK;
 }
@@ -158,4 +168,19 @@
 	}
 
+	rc = ddf_fun_unbind(fun);
+	if (rc != EOK) {
+		ddf_msg(LVL_ERROR, "Failed unbinding function '%s'.", name);
+		return rc;
+	}
+
+	ddf_fun_destroy(fun);
+	return EOK;
+}
+
+static int fun_unbind(ddf_fun_t *fun, const char *name)
+{
+	int rc;
+
+	ddf_msg(LVL_DEBUG, "fun_unbind(%p, '%s')", fun, name);
 	rc = ddf_fun_unbind(fun);
 	if (rc != EOK) {
@@ -184,5 +209,5 @@
 
 	if (str_cmp(dev->name, "child") != 0) {
-		fid_t postpone = fibril_create(postponed_birth, test2);
+		fid_t postpone = fibril_create(plug_unplug, test2);
 		if (postpone == 0) {
 			ddf_msg(LVL_ERROR, "fibril_create() failed.");
@@ -232,4 +257,38 @@
 }
 
+static int test2_dev_gone(ddf_dev_t *dev)
+{
+	test2_t *test2 = (test2_t *)dev->driver_data;
+	int rc;
+
+	ddf_msg(LVL_DEBUG, "test2_dev_gone(%p)", dev);
+
+	if (test2->fun_a != NULL) {
+		rc = fun_unbind(test2->fun_a, "a");
+		if (rc != EOK)
+			return rc;
+	}
+
+	if (test2->fun_err != NULL) {
+		rc = fun_unbind(test2->fun_err, "ERROR");
+		if (rc != EOK)
+			return rc;
+	}
+
+	if (test2->child != NULL) {
+		rc = fun_unbind(test2->child, "child");
+		if (rc != EOK)
+			return rc;
+	}
+
+	if (test2->test1 != NULL) {
+		rc = fun_unbind(test2->test1, "test1");
+		if (rc != EOK)
+			return rc;
+	}
+
+	return EOK;
+}
+
 
 static int test2_fun_online(ddf_fun_t *fun)
@@ -248,5 +307,5 @@
 {
 	printf(NAME ": HelenOS test2 virtual device driver\n");
-	ddf_log_init(NAME, LVL_ERROR);
+	ddf_log_init(NAME, LVL_NOTE);
 	return ddf_driver_main(&test2_driver);
 }
Index: uspace/lib/c/include/ipc/devman.h
===================================================================
--- uspace/lib/c/include/ipc/devman.h	(revision 16cc9a67c9e8675b44eda89eeeb9e3971f7905f7)
+++ uspace/lib/c/include/ipc/devman.h	(revision 80a96d2355e43d996baf70b026fae98a9d19add6)
@@ -147,7 +147,7 @@
 	DRIVER_DEV_ADD = IPC_FIRST_USER_METHOD,
 	DRIVER_DEV_REMOVE,
+	DRIVER_DEV_GONE,
 	DRIVER_FUN_ONLINE,
 	DRIVER_FUN_OFFLINE,
-
 } devman_to_driver_t;
 
Index: uspace/lib/drv/generic/driver.c
===================================================================
--- uspace/lib/drv/generic/driver.c	(revision 16cc9a67c9e8675b44eda89eeeb9e3971f7905f7)
+++ uspace/lib/drv/generic/driver.c	(revision 80a96d2355e43d996baf70b026fae98a9d19add6)
@@ -333,4 +333,34 @@
 }
 
+static void driver_dev_gone(ipc_callid_t iid, ipc_call_t *icall)
+{
+	devman_handle_t devh;
+	ddf_dev_t *dev;
+	int rc;
+	
+	devh = IPC_GET_ARG1(*icall);
+	
+	fibril_mutex_lock(&devices_mutex);
+	dev = driver_get_device(devh);
+	if (dev != NULL)
+		dev_add_ref(dev);
+	fibril_mutex_unlock(&devices_mutex);
+	
+	if (dev == NULL) {
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	if (driver->driver_ops->dev_gone != NULL)
+		rc = driver->driver_ops->dev_gone(dev);
+	else
+		rc = ENOTSUP;
+	
+	if (rc == EOK)
+		dev_del_ref(dev);
+	
+	async_answer_0(iid, (sysarg_t) rc);
+}
+
 static void driver_fun_online(ipc_callid_t iid, ipc_call_t *icall)
 {
@@ -423,4 +453,7 @@
 		case DRIVER_DEV_REMOVE:
 			driver_dev_remove(callid, &call);
+			break;
+		case DRIVER_DEV_GONE:
+			driver_dev_gone(callid, &call);
 			break;
 		case DRIVER_FUN_ONLINE:
Index: uspace/lib/drv/include/ddf/driver.h
===================================================================
--- uspace/lib/drv/include/ddf/driver.h	(revision 16cc9a67c9e8675b44eda89eeeb9e3971f7905f7)
+++ uspace/lib/drv/include/ddf/driver.h	(revision 80a96d2355e43d996baf70b026fae98a9d19add6)
@@ -139,4 +139,6 @@
 	/** Ask driver to remove a device */
 	int (*dev_remove)(ddf_dev_t *);
+	/** Inform driver a device disappeared */
+	int (*dev_gone)(ddf_dev_t *);
 	/** Ask driver to online a specific function */
 	int (*fun_online)(ddf_fun_t *);
Index: uspace/srv/devman/devman.c
===================================================================
--- uspace/srv/devman/devman.c	(revision 16cc9a67c9e8675b44eda89eeeb9e3971f7905f7)
+++ uspace/srv/devman/devman.c	(revision 80a96d2355e43d996baf70b026fae98a9d19add6)
@@ -878,5 +878,27 @@
 	
 	return retval;
-
+}
+
+int driver_dev_gone(dev_tree_t *tree, dev_node_t *dev)
+{
+	async_exch_t *exch;
+	sysarg_t retval;
+	driver_t *drv;
+	devman_handle_t handle;
+	
+	assert(dev != NULL);
+	
+	log_msg(LVL_DEBUG, "driver_dev_gone(%p)", dev);
+	
+	fibril_rwlock_read_lock(&tree->rwlock);
+	drv = dev->drv;
+	handle = dev->handle;
+	fibril_rwlock_read_unlock(&tree->rwlock);
+	
+	exch = async_exchange_begin(drv->sess);
+	retval = async_req_1_0(exch, DRIVER_DEV_GONE, handle);
+	async_exchange_end(exch);
+	
+	return retval;
 }
 
Index: uspace/srv/devman/devman.h
===================================================================
--- uspace/srv/devman/devman.h	(revision 16cc9a67c9e8675b44eda89eeeb9e3971f7905f7)
+++ uspace/srv/devman/devman.h	(revision 80a96d2355e43d996baf70b026fae98a9d19add6)
@@ -263,4 +263,5 @@
 extern bool start_driver(driver_t *);
 extern int driver_dev_remove(dev_tree_t *, dev_node_t *);
+extern int driver_dev_gone(dev_tree_t *, dev_node_t *);
 extern int driver_fun_online(dev_tree_t *, fun_node_t *);
 extern int driver_fun_offline(dev_tree_t *, fun_node_t *);
Index: uspace/srv/devman/main.c
===================================================================
--- uspace/srv/devman/main.c	(revision 16cc9a67c9e8675b44eda89eeeb9e3971f7905f7)
+++ uspace/srv/devman/main.c	(revision 80a96d2355e43d996baf70b026fae98a9d19add6)
@@ -316,10 +316,13 @@
 		if (fun->child != NULL) {
 			dev_node_t *dev = fun->child;
+			device_state_t dev_state;
 			
 			dev_add_ref(dev);
+			dev_state = dev->state;
+			
 			fibril_rwlock_write_unlock(&device_tree.rwlock);
-			
+
 			/* If device is owned by driver, ask driver to give it up. */
-			if (dev->state == DEVICE_USABLE) {
+			if (dev_state == DEVICE_USABLE) {
 				rc = driver_dev_remove(&device_tree, dev);
 				if (rc != EOK) {
@@ -333,6 +336,8 @@
 			if (!list_empty(&dev->functions)) {
 				fibril_rwlock_read_unlock(&device_tree.rwlock);
+				dev_del_ref(dev);
 				return EIO;
 			}
+			
 			driver_t *driver = dev->drv;
 			fibril_rwlock_read_unlock(&device_tree.rwlock);
@@ -579,5 +584,4 @@
 	int rc;
 	
-	
 	fun_node_t *fun = find_fun_node(&device_tree, fun_handle);
 	if (fun == NULL) {
@@ -598,9 +602,53 @@
 	
 	if (fun->ftype == fun_inner) {
-		/* Handle possible descendants */
-		/* TODO - This is a surprise removal */
+		/* This is a surprise removal. Handle possible descendants */
 		if (fun->child != NULL) {
-			log_msg(LVL_WARN, "devman_remove_function(): not handling "
-			    "descendants\n");
+			dev_node_t *dev = fun->child;
+			device_state_t dev_state;
+			int gone_rc;
+			
+			dev_add_ref(dev);
+			dev_state = dev->state;
+			
+			fibril_rwlock_write_unlock(&device_tree.rwlock);
+			
+			/* If device is owned by driver, inform driver it is gone. */
+			if (dev_state == DEVICE_USABLE)
+				gone_rc = driver_dev_gone(&device_tree, dev);
+			else
+				gone_rc = EOK;
+			
+			fibril_rwlock_read_lock(&device_tree.rwlock);
+			
+			/* Verify that driver succeeded and removed all functions */
+			if (gone_rc != EOK || !list_empty(&dev->functions)) {
+				log_msg(LVL_ERROR, "Driver did not remove "
+				    "functions for device that is gone. "
+				    "Device node is now defunct.");
+				
+				/*
+				 * Not much we can do but mark the device
+				 * node as having invalid state. This
+				 * is a driver bug.
+				 */
+				dev->state = DEVICE_INVALID;
+				fibril_rwlock_read_unlock(&device_tree.rwlock);
+				dev_del_ref(dev);
+				return;
+			}
+			
+			driver_t *driver = dev->drv;
+			fibril_rwlock_read_unlock(&device_tree.rwlock);
+			
+			if (driver)
+				detach_driver(&device_tree, dev);
+			
+			fibril_rwlock_write_lock(&device_tree.rwlock);
+			remove_dev_node(&device_tree, dev);
+			
+			/* Delete ref created when node was inserted */
+			dev_del_ref(dev);
+			/* Delete ref created by dev_add_ref(dev) above */
+			dev_del_ref(dev);
 		}
 	} else {
