Index: uspace/lib/c/generic/devman.c
===================================================================
--- uspace/lib/c/generic/devman.c	(revision 763e0cddf0e1c4057130bf42fd3ecf4c6658bc63)
+++ uspace/lib/c/generic/devman.c	(revision d0dd7b558f0c1e5da6887176956a13225dd0f05e)
@@ -308,4 +308,23 @@
 }
 
+/** 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 negative error code.
+ */
+int devman_remove_function(devman_handle_t funh)
+{
+	async_exch_t *exch;
+	sysarg_t retval;
+	
+	exch = devman_exchange_begin_blocking(DEVMAN_DRIVER);
+	retval = async_req_1_0(exch, DEVMAN_REMOVE_FUNCTION, (sysarg_t) 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)
Index: uspace/lib/c/generic/loc.c
===================================================================
--- uspace/lib/c/generic/loc.c	(revision 763e0cddf0e1c4057130bf42fd3ecf4c6658bc63)
+++ uspace/lib/c/generic/loc.c	(revision d0dd7b558f0c1e5da6887176956a13225dd0f05e)
@@ -267,7 +267,7 @@
 }
 
-/** Register new device.
- *
- * The @p interface is used when forwarding connection to the driver.
+/** Register new service.
+ *
+ * The @p interface is used when forwarding connection to the server.
  * If not 0, the first argument is the interface and the second argument
  * is the service ID.
@@ -276,11 +276,11 @@
  * the handle (to ensure backward compatibility).
  *
- * @param      fqdn      Fully qualified device name.
- * @param[out] handle    Handle to the created instance of device.
- * @param      interface Interface when forwarding.
- *
- */
-int loc_service_register_with_iface(const char *fqdn,
-    service_id_t *handle, sysarg_t interface)
+ * @param      fqsn      Fully qualified service name
+ * @param[out] sid       Service ID of new service
+ * @param      interface Interface when forwarding
+ *
+ */
+int loc_service_register_with_iface(const char *fqsn,
+    service_id_t *sid, sysarg_t interface)
 {
 	async_exch_t *exch = loc_exchange_begin_blocking(LOC_PORT_SUPPLIER);
@@ -289,5 +289,5 @@
 	aid_t req = async_send_2(exch, LOC_SERVICE_REGISTER, interface, 0,
 	    &answer);
-	sysarg_t retval = async_data_write_start(exch, fqdn, str_size(fqdn));
+	sysarg_t retval = async_data_write_start(exch, fqsn, str_size(fqsn));
 	
 	loc_exchange_end(exch);
@@ -301,25 +301,41 @@
 	
 	if (retval != EOK) {
-		if (handle != NULL)
-			*handle = -1;
-		
-		return retval;
-	}
-	
-	if (handle != NULL)
-		*handle = (service_id_t) IPC_GET_ARG1(answer);
+		if (sid != NULL)
+			*sid = -1;
+		
+		return retval;
+	}
+	
+	if (sid != NULL)
+		*sid = (service_id_t) IPC_GET_ARG1(answer);
 	
 	return retval;
 }
 
-/** Register new device.
- *
- * @param fqdn   Fully qualified device name.
- * @param handle Output: Handle to the created instance of device.
- *
- */
-int loc_service_register(const char *fqdn, service_id_t *handle)
-{
-	return loc_service_register_with_iface(fqdn, handle, 0);
+/** Register new service.
+ *
+ * @param fqsn	Fully qualified service name
+ * @param sid	Output: ID of new service
+ *
+ */
+int loc_service_register(const char *fqdn, service_id_t *sid)
+{
+	return loc_service_register_with_iface(fqdn, sid, 0);
+}
+
+/** Unregister service.
+ *
+ * @param sid	Service ID
+ */
+int loc_service_unregister(service_id_t sid)
+{
+	async_exch_t *exch;
+	sysarg_t retval;
+	
+	exch = loc_exchange_begin_blocking(LOC_PORT_SUPPLIER);
+	retval = async_req_1_0(exch, LOC_SERVICE_UNREGISTER, sid);
+	loc_exchange_end(exch);
+	
+	return (int)retval;
 }
 
Index: uspace/lib/c/include/devman.h
===================================================================
--- uspace/lib/c/include/devman.h	(revision 763e0cddf0e1c4057130bf42fd3ecf4c6658bc63)
+++ uspace/lib/c/include/devman.h	(revision d0dd7b558f0c1e5da6887176956a13225dd0f05e)
@@ -49,4 +49,5 @@
 extern int devman_add_function(const char *, fun_type_t, match_id_list_t *,
     devman_handle_t, devman_handle_t *);
+extern int devman_remove_function(devman_handle_t);
 
 extern async_sess_t *devman_device_connect(exch_mgmt_t, devman_handle_t,
Index: uspace/lib/c/include/ipc/devman.h
===================================================================
--- uspace/lib/c/include/ipc/devman.h	(revision 763e0cddf0e1c4057130bf42fd3ecf4c6658bc63)
+++ uspace/lib/c/include/ipc/devman.h	(revision d0dd7b558f0c1e5da6887176956a13225dd0f05e)
@@ -138,6 +138,6 @@
 	DEVMAN_ADD_FUNCTION,
 	DEVMAN_ADD_MATCH_ID,
-	DEVMAN_ADD_DEVICE_TO_CATEGORY
-
+	DEVMAN_ADD_DEVICE_TO_CATEGORY,
+	DEVMAN_REMOVE_FUNCTION
 } driver_to_devman_t;
 
Index: uspace/lib/c/include/loc.h
===================================================================
--- uspace/lib/c/include/loc.h	(revision 763e0cddf0e1c4057130bf42fd3ecf4c6658bc63)
+++ uspace/lib/c/include/loc.h	(revision d0dd7b558f0c1e5da6887176956a13225dd0f05e)
@@ -50,4 +50,5 @@
 extern int loc_service_register_with_iface(const char *, service_id_t *,
     sysarg_t);
+extern int loc_service_unregister(service_id_t);
 extern int loc_service_add_to_cat(service_id_t, category_id_t);
 
Index: uspace/lib/drv/generic/driver.c
===================================================================
--- uspace/lib/drv/generic/driver.c	(revision 763e0cddf0e1c4057130bf42fd3ecf4c6658bc63)
+++ uspace/lib/drv/generic/driver.c	(revision d0dd7b558f0c1e5da6887176956a13225dd0f05e)
@@ -582,4 +582,5 @@
 int ddf_fun_bind(ddf_fun_t *fun)
 {
+	assert(fun->bound == false);
 	assert(fun->name != NULL);
 	
@@ -596,4 +597,29 @@
 	fun->bound = true;
 	return res;
+}
+
+/** Unbind a function node.
+ *
+ * Unbind the specified function from the system. This effectively makes
+ * the function invisible to the system.
+ *
+ * @param fun		Function to bind
+ * @return		EOK on success or negative error code
+ */
+int ddf_fun_unbind(ddf_fun_t *fun)
+{
+	int res;
+	
+	assert(fun->bound == true);
+	
+	add_to_functions_list(fun);
+	res = devman_remove_function(fun->handle);
+	if (res != EOK)
+		return res;
+
+	remove_from_functions_list(fun);
+	
+	fun->bound = false;
+	return EOK;
 }
 
Index: uspace/lib/drv/include/ddf/driver.h
===================================================================
--- uspace/lib/drv/include/ddf/driver.h	(revision 763e0cddf0e1c4057130bf42fd3ecf4c6658bc63)
+++ uspace/lib/drv/include/ddf/driver.h	(revision d0dd7b558f0c1e5da6887176956a13225dd0f05e)
@@ -149,4 +149,5 @@
 extern void ddf_fun_destroy(ddf_fun_t *);
 extern int ddf_fun_bind(ddf_fun_t *);
+extern int ddf_fun_unbind(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 763e0cddf0e1c4057130bf42fd3ecf4c6658bc63)
+++ uspace/srv/devman/devman.c	(revision d0dd7b558f0c1e5da6887176956a13225dd0f05e)
@@ -1100,4 +1100,24 @@
 }
 
+/** Remove function from device tree.
+ *
+ * @param tree		Device tree
+ * @param node		Function node to remove
+ */
+void remove_fun_node(dev_tree_t *tree, fun_node_t *fun)
+{
+	assert(tree != NULL);
+	assert(fun != NULL);
+	assert(fibril_rwlock_is_write_locked(&tree->rwlock));
+	
+	/* Remove the node from the handle-to-node map. */
+	unsigned long key = fun->handle;
+	hash_table_remove(&tree->devman_functions, &key, 1);
+	
+	/* Remove the node from the list of its parent's children. */
+	if (fun->dev != NULL)
+		list_remove(&fun->dev_functions);
+}
+
 /** Find function node with a specified path in the device tree.
  * 
Index: uspace/srv/devman/devman.h
===================================================================
--- uspace/srv/devman/devman.h	(revision 763e0cddf0e1c4057130bf42fd3ecf4c6658bc63)
+++ uspace/srv/devman/devman.h	(revision d0dd7b558f0c1e5da6887176956a13225dd0f05e)
@@ -1,4 +1,5 @@
 /*
  * Copyright (c) 2010 Lenka Trochtova
+ * Copyright (c) 2011 Jiri Svoboda
  * All rights reserved.
  *
@@ -154,4 +155,6 @@
 	/** Name of the function, assigned by the device driver */
 	char *name;
+	/** Function type */
+	fun_type_t ftype;
 	
 	/** Full path and name of the device in device hierarchy */
@@ -265,4 +268,5 @@
 extern bool insert_dev_node(dev_tree_t *, dev_node_t *, fun_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 *);
 
 /* Loc services */
Index: uspace/srv/devman/main.c
===================================================================
--- uspace/srv/devman/main.c	(revision 763e0cddf0e1c4057130bf42fd3ecf4c6658bc63)
+++ uspace/srv/devman/main.c	(revision d0dd7b558f0c1e5da6887176956a13225dd0f05e)
@@ -278,6 +278,8 @@
 		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);
@@ -367,4 +369,46 @@
 }
 
+/** Remove function. */
+static void devman_remove_function(ipc_callid_t callid, ipc_call_t *call)
+{
+	devman_handle_t fun_handle = IPC_GET_ARG1(*call);
+	dev_tree_t *tree = &device_tree;
+	int rc;
+	
+	fibril_rwlock_write_lock(&tree->rwlock);
+	
+	fun_node_t *fun = find_fun_node_no_lock(&device_tree, fun_handle);
+	if (fun == NULL) {
+		fibril_rwlock_write_unlock(&tree->rwlock);
+		async_answer_0(callid, ENOENT);
+		return;
+	}
+	
+	log_msg(LVL_DEBUG, "devman_remove_function(fun='%s')", fun->pathname);
+	
+	if (fun->ftype == fun_inner) {
+		/* Handle possible descendants */
+		/* TODO */
+		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;
+		}
+	}
+	
+	remove_fun_node(&device_tree, fun);
+	fibril_rwlock_write_unlock(&tree->rwlock);
+	delete_fun_node(fun);
+	
+	log_msg(LVL_DEBUG, "devman_remove_function() succeeded.");
+	async_answer_0(callid, EOK);
+}
+
 /** Initialize driver which has registered itself as running and ready.
  *
@@ -418,4 +462,7 @@
 		case DEVMAN_ADD_DEVICE_TO_CATEGORY:
 			devman_add_function_to_cat(callid, &call);
+			break;
+		case DEVMAN_REMOVE_FUNCTION:
+			devman_remove_function(callid, &call);
 			break;
 		default:
Index: uspace/srv/loc/category.c
===================================================================
--- uspace/srv/loc/category.c	(revision 763e0cddf0e1c4057130bf42fd3ecf4c6658bc63)
+++ uspace/srv/loc/category.c	(revision d0dd7b558f0c1e5da6887176956a13225dd0f05e)
@@ -93,5 +93,5 @@
 	cat->id = loc_create_id();
 	link_initialize(&cat->cat_list);
-	list_initialize(&cat->services);
+	list_initialize(&cat->svc_memb);
 }
 
@@ -113,17 +113,38 @@
 {
 	assert(fibril_mutex_is_locked(&cat->mutex));
+	assert(fibril_mutex_is_locked(&services_list_mutex));
 
 	/* Verify that category does not contain this service yet. */
-	list_foreach(cat->services, item) {
-
-		loc_service_t *csvc = list_get_instance(item, loc_service_t,
-		    cat_services);
-		if (csvc == svc) {
+	list_foreach(cat->svc_memb, item) {
+		svc_categ_t *memb = list_get_instance(item, svc_categ_t,
+		    cat_link);
+		if (memb->svc == svc) {
 			return EEXIST;
 		}
 	}
 
-	list_append(&svc->cat_services, &cat->services);
+	svc_categ_t *nmemb = malloc(sizeof(svc_categ_t));
+	if (nmemb == NULL)
+		return ENOMEM;
+
+	nmemb->svc = svc;
+	nmemb->cat = cat;
+
+	list_append(&nmemb->cat_link, &cat->svc_memb);
+	list_append(&nmemb->svc_link, &svc->cat_memb);
+
 	return EOK;
+}
+
+/** Remove service from category. */
+void category_remove_service(svc_categ_t *memb)
+{
+	assert(fibril_mutex_is_locked(&memb->cat->mutex));
+	assert(fibril_mutex_is_locked(&services_list_mutex));
+
+	list_remove(&memb->cat_link);
+	list_remove(&memb->svc_link);
+
+	free(memb);
 }
 
@@ -169,5 +190,5 @@
 	buf_cnt = buf_size / sizeof(service_id_t);
 
-	act_cnt = list_count(&cat->services);
+	act_cnt = list_count(&cat->svc_memb);
 	*act_size = act_cnt * sizeof(service_id_t);
 
@@ -176,10 +197,10 @@
 
 	size_t pos = 0;
-	list_foreach(cat->services, item) {
-		loc_service_t *svc =
-		    list_get_instance(item, loc_service_t, cat_services);
+	list_foreach(cat->svc_memb, item) {
+		svc_categ_t *memb =
+		    list_get_instance(item, svc_categ_t, cat_link);
 
 		if (pos < buf_cnt)
-			id_buf[pos] = svc->id;
+			id_buf[pos] = memb->svc->id;
 		pos++;
 	}
Index: uspace/srv/loc/category.h
===================================================================
--- uspace/srv/loc/category.h	(revision 763e0cddf0e1c4057130bf42fd3ecf4c6658bc63)
+++ uspace/srv/loc/category.h	(revision d0dd7b558f0c1e5da6887176956a13225dd0f05e)
@@ -55,6 +55,6 @@
 	link_t cat_list;
 
-	/** List of services in this category (loc_service_t) */
-	list_t services;
+	/** List of service memberships in this category (svc_categ_t) */
+	list_t svc_memb;
 } category_t;
 
@@ -67,4 +67,17 @@
 } categ_dir_t;
 
+/** Service in category membership. */
+typedef struct {
+	/** Link to category_t.svc_memb list */
+	link_t cat_link;
+	/** Link to loc_service_t.cat_memb list */
+	link_t svc_link;
+	
+	/** Category */
+	category_t *cat;
+	/** Service */
+	loc_service_t *svc;
+} svc_categ_t;
+
 extern void categ_dir_init(categ_dir_t *);
 extern void categ_dir_add_cat(categ_dir_t *, category_t *);
@@ -73,4 +86,5 @@
 extern category_t *category_new(const char *);
 extern int category_add_service(category_t *, loc_service_t *);
+extern void category_remove_service(svc_categ_t *);
 extern category_t *category_get(categ_dir_t *, catid_t);
 extern category_t *category_find_by_name(categ_dir_t *, const char *);
Index: uspace/srv/loc/loc.c
===================================================================
--- uspace/srv/loc/loc.c	(revision 763e0cddf0e1c4057130bf42fd3ecf4c6658bc63)
+++ uspace/srv/loc/loc.c	(revision d0dd7b558f0c1e5da6887176956a13225dd0f05e)
@@ -67,5 +67,5 @@
  **/
 
-static FIBRIL_MUTEX_INITIALIZE(services_list_mutex);
+FIBRIL_MUTEX_INITIALIZE(services_list_mutex);
 static FIBRIL_CONDVAR_INITIALIZE(services_list_cv);
 static FIBRIL_MUTEX_INITIALIZE(servers_list_mutex);
@@ -311,8 +311,19 @@
 {
 	assert(fibril_mutex_is_locked(&services_list_mutex));
-
+	assert(fibril_mutex_is_locked(&cdir.mutex));
+	
 	loc_namespace_delref(service->namespace);
 	list_remove(&(service->services));
 	list_remove(&(service->server_services));
+	
+	/* Remove service from all categories. */
+	while (!list_empty(&service->cat_memb)) {
+		link_t *link = list_first(&service->cat_memb);
+		svc_categ_t *memb = list_get_instance(link, svc_categ_t,
+		    svc_link);
+		fibril_mutex_lock(&memb->cat->mutex);
+		category_remove_service(memb);
+		fibril_mutex_unlock(&memb->cat->mutex);
+	}
 	
 	free(service->name);
@@ -415,4 +426,5 @@
 	fibril_mutex_lock(&services_list_mutex);
 	fibril_mutex_lock(&server->services_mutex);
+	fibril_mutex_lock(&cdir.mutex);
 	
 	while (!list_empty(&server->services)) {
@@ -423,4 +435,5 @@
 	}
 	
+	fibril_mutex_unlock(&cdir.mutex);
 	fibril_mutex_unlock(&server->services_mutex);
 	fibril_mutex_unlock(&services_list_mutex);
@@ -492,4 +505,5 @@
 	link_initialize(&service->services);
 	link_initialize(&service->server_services);
+	list_initialize(&service->cat_memb);
 	
 	/* Check that service is not already registered */
@@ -529,9 +543,22 @@
  *
  */
-static int loc_service_unregister(ipc_callid_t iid, ipc_call_t *icall, 
+static void loc_service_unregister(ipc_callid_t iid, ipc_call_t *icall, 
     loc_server_t *server)
 {
-	/* TODO */
-	return EOK;
+	loc_service_t *svc;
+	
+	fibril_mutex_lock(&services_list_mutex);
+	svc = loc_service_find_id(IPC_GET_ARG1(*icall));
+	if (svc == NULL) {
+		fibril_mutex_unlock(&services_list_mutex);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	fibril_mutex_lock(&cdir.mutex);
+	loc_service_unregister_core(svc);
+	fibril_mutex_unlock(&cdir.mutex);
+	fibril_mutex_unlock(&services_list_mutex);
+	async_answer_0(iid, EOK);
 }
 
@@ -1176,5 +1203,7 @@
 	
 	fibril_mutex_lock(&services_list_mutex);
+	fibril_mutex_lock(&cdir.mutex);
 	loc_service_unregister_core(null_services[i]);
+	fibril_mutex_unlock(&cdir.mutex);
 	fibril_mutex_unlock(&services_list_mutex);
 	
@@ -1242,4 +1271,7 @@
 
 	cat = category_new("usbhc");
+	categ_dir_add_cat(&cdir, cat);
+
+	cat = category_new("virtual");
 	categ_dir_add_cat(&cdir, cat);
 
Index: uspace/srv/loc/loc.h
===================================================================
--- uspace/srv/loc/loc.h	(revision 763e0cddf0e1c4057130bf42fd3ecf4c6658bc63)
+++ uspace/srv/loc/loc.h	(revision d0dd7b558f0c1e5da6887176956a13225dd0f05e)
@@ -89,4 +89,6 @@
 	/** Link to list of services in category (category_t.services) */
 	link_t cat_services;
+	/** List of category memberships (svc_categ_t) */
+	list_t cat_memb;
 	/** Unique service identifier */
 	service_id_t id;
@@ -101,4 +103,6 @@
 } loc_service_t;
 
+extern fibril_mutex_t services_list_mutex;
+
 extern service_id_t loc_create_id(void);
 extern void loc_category_change_event(void);
