Index: kernel/generic/src/ipc/sysipc.c
===================================================================
--- kernel/generic/src/ipc/sysipc.c	(revision 86ffa27f45682aa2a5736f665f400ad262ce5baf)
+++ kernel/generic/src/ipc/sysipc.c	(revision cc574511968ab8b7b5afe2cbb60c2053608cc393)
@@ -427,6 +427,4 @@
 	case IPC_M_DATA_READ: {
 		size_t size = IPC_GET_ARG2(call->data);
-		if (size <= 0)
-			return ELIMIT;
 		if (size > DATA_XFER_LIMIT) {
 			int flags = IPC_GET_ARG3(call->data);
Index: uspace/lib/c/generic/loc.c
===================================================================
--- uspace/lib/c/generic/loc.c	(revision 86ffa27f45682aa2a5736f665f400ad262ce5baf)
+++ uspace/lib/c/generic/loc.c	(revision cc574511968ab8b7b5afe2cbb60c2053608cc393)
@@ -1,5 +1,5 @@
 /*
  * Copyright (c) 2007 Josef Cejka
- * Copyright (c) 2009 Jiri Svoboda
+ * Copyright (c) 2011 Jiri Svoboda
  * All rights reserved.
  *
@@ -331,4 +331,54 @@
 }
 
+/** Get category ID.
+ *
+ * Provided name of a category, return its ID.
+ *
+ * @param name		Category name
+ * @param cat_id	Place to store ID
+ * @param flags		IPC_FLAG_BLOCKING to wait for location service to start
+ * @return		EOK on success or negative error code
+ */
+int loc_category_get_id(const char *name, category_id_t *cat_id,
+    unsigned int flags)
+{
+	async_exch_t *exch;
+	
+	if (flags & IPC_FLAG_BLOCKING)
+		exch = loc_exchange_begin_blocking(LOC_PORT_CONSUMER);
+	else {
+		exch = loc_exchange_begin(LOC_PORT_CONSUMER);
+		if (exch == NULL)
+			return errno;
+	}
+	
+	ipc_call_t answer;
+	aid_t req = async_send_0(exch, LOC_CATEGORY_GET_ID,
+	    &answer);
+	sysarg_t retval = async_data_write_start(exch, name, str_size(name));
+	
+	loc_exchange_end(exch);
+	
+	if (retval != EOK) {
+		async_wait_for(req, NULL);
+		return retval;
+	}
+	
+	async_wait_for(req, &retval);
+	
+	if (retval != EOK) {
+		if (cat_id != NULL)
+			*cat_id = (category_id_t) -1;
+		
+		return retval;
+	}
+	
+	if (cat_id != NULL)
+		*cat_id = (category_id_t) IPC_GET_ARG1(answer);
+	
+	return retval;
+}
+
+
 loc_object_type_t loc_id_probe(service_id_t handle)
 {
@@ -393,4 +443,22 @@
 }
 
+/** Add service to category.
+ *
+ * @param svc_id	Service ID
+ * @param cat_id	Category ID
+ * @return		EOK on success or negative error code
+ */
+int loc_service_add_to_cat(service_id_t svc_id, service_id_t cat_id)
+{
+	async_exch_t *exch;
+	sysarg_t retval;
+	
+	exch = loc_exchange_begin_blocking(LOC_PORT_SUPPLIER);
+	retval = async_req_2_0(exch, LOC_SERVICE_ADD_TO_CAT, svc_id, cat_id);
+	loc_exchange_end(exch);
+	
+	return retval;
+}
+
 static size_t loc_count_services_internal(async_exch_t *exch,
     service_id_t ns_handle)
@@ -425,5 +493,5 @@
 size_t loc_get_namespaces(loc_sdesc_t **data)
 {
-	/* Loop until namespaces read succesful */
+	/* Loop until read is succesful */
 	while (true) {
 		async_exch_t *exch = loc_exchange_begin_blocking(LOC_PORT_CONSUMER);
@@ -474,5 +542,5 @@
 size_t loc_get_services(service_id_t ns_handle, loc_sdesc_t **data)
 {
-	/* Loop until devices read succesful */
+	/* Loop until read is succesful */
 	while (true) {
 		async_exch_t *exch = loc_exchange_begin_blocking(LOC_PORT_CONSUMER);
@@ -520,2 +588,81 @@
 	}
 }
+
+static int loc_category_get_svcs_internal(category_id_t cat_id,
+    service_id_t *id_buf, size_t buf_size, size_t *act_size)
+{
+	async_exch_t *exch = loc_exchange_begin_blocking(LOC_PORT_CONSUMER);
+
+	ipc_call_t answer;
+	aid_t req = async_send_1(exch, LOC_CATEGORY_GET_SVCS, cat_id,
+	    &answer);
+	int rc = async_data_read_start(exch, id_buf, buf_size);
+	
+	loc_exchange_end(exch);
+	
+	if (rc != EOK) {
+		async_wait_for(req, NULL);
+		return rc;
+	}
+	
+	sysarg_t retval;
+	async_wait_for(req, &retval);
+	
+	if (retval != EOK) {
+		return retval;
+	}
+	
+	*act_size = IPC_GET_ARG1(answer);
+	return EOK;
+}
+
+/** Get list of services in category.
+ *
+ * Returns an allocated array of service IDs.
+ *
+ * @param cat_id	Category ID
+ * @param data		Place to store pointer to array of IDs
+ * @param count		Place to store number of IDs
+ * @return 		EOK on success or negative error code
+ */
+int loc_category_get_svcs(category_id_t cat_id, category_id_t **data,
+    size_t *count)
+{
+	service_id_t *ids;
+	size_t act_size;
+	size_t alloc_size;
+	int rc;
+
+	*data = NULL;
+	act_size = 0;	/* silence warning */
+
+	rc = loc_category_get_svcs_internal(cat_id, NULL, 0, &act_size);
+	if (rc != EOK)
+		return rc;
+
+	alloc_size = act_size;
+	ids = malloc(alloc_size);
+	if (ids == NULL)
+		return ENOMEM;
+
+	while (true) {
+		rc = loc_category_get_svcs_internal(cat_id, ids, alloc_size,
+		    &act_size);
+		if (rc != EOK)
+			return rc;
+
+		if (act_size <= alloc_size)
+			break;
+
+		alloc_size *= 2;
+		free(ids);
+
+		ids = malloc(alloc_size);
+		if (ids == NULL)
+			return ENOMEM;
+	}
+
+	*count = act_size / sizeof(category_id_t);
+	*data = ids;
+	return EOK;
+}
Index: uspace/lib/c/include/ipc/loc.h
===================================================================
--- uspace/lib/c/include/ipc/loc.h	(revision 86ffa27f45682aa2a5736f665f400ad262ce5baf)
+++ uspace/lib/c/include/ipc/loc.h	(revision cc574511968ab8b7b5afe2cbb60c2053608cc393)
@@ -40,4 +40,5 @@
 
 typedef sysarg_t service_id_t;
+typedef sysarg_t category_id_t;
 
 typedef enum {
@@ -50,8 +51,11 @@
 	LOC_SERVER_REGISTER = IPC_FIRST_USER_METHOD,
 	LOC_SERVER_UNREGISTER,
+	LOC_SERVICE_ADD_TO_CAT,
 	LOC_SERVICE_REGISTER,
 	LOC_SERVICE_UNREGISTER,
 	LOC_SERVICE_GET_ID,
 	LOC_NAMESPACE_GET_ID,
+	LOC_CATEGORY_GET_ID,
+	LOC_CATEGORY_GET_SVCS,
 	LOC_ID_PROBE,
 	LOC_NULL_CREATE,
Index: uspace/lib/c/include/loc.h
===================================================================
--- uspace/lib/c/include/loc.h	(revision 86ffa27f45682aa2a5736f665f400ad262ce5baf)
+++ uspace/lib/c/include/loc.h	(revision cc574511968ab8b7b5afe2cbb60c2053608cc393)
@@ -48,4 +48,5 @@
 extern int loc_service_register_with_iface(const char *, service_id_t *,
     sysarg_t);
+extern int loc_service_add_to_cat(service_id_t, category_id_t);
 
 extern int loc_service_get_id(const char *, service_id_t *,
@@ -53,4 +54,7 @@
 extern int loc_namespace_get_id(const char *, service_id_t *,
     unsigned int);
+extern int loc_category_get_id(const char *, category_id_t *,
+    unsigned int);
+extern int loc_category_get_svcs(category_id_t, category_id_t **, size_t *);
 extern loc_object_type_t loc_id_probe(service_id_t);
 
Index: uspace/srv/devman/main.c
===================================================================
--- uspace/srv/devman/main.c	(revision 86ffa27f45682aa2a5736f665f400ad262ce5baf)
+++ uspace/srv/devman/main.c	(revision cc574511968ab8b7b5afe2cbb60c2053608cc393)
@@ -362,8 +362,10 @@
 {
 	devman_handle_t handle = IPC_GET_ARG1(*call);
+	category_id_t cat_id;
+	int rc;
 	
 	/* Get class name. */
 	char *class_name;
-	int rc = async_data_write_accept((void **) &class_name, true,
+	rc = async_data_write_accept((void **) &class_name, true,
 	    0, 0, 0, 0);
 	if (rc != EOK) {
@@ -383,4 +385,12 @@
 	/* Register the device's class alias with location service. */
 	loc_register_class_dev(class_info);
+	
+	rc = loc_category_get_id(class_name, &cat_id, IPC_FLAG_BLOCKING);
+	if (rc == EOK) {
+		loc_service_add_to_cat(fun->service_id, cat_id);
+	} else {
+		log_msg(LVL_ERROR, "Failed adding function `%s' to category "
+		    "`%s'.", fun->pathname, class_name);
+	}
 	
 	log_msg(LVL_NOTE, "Function `%s' added to class `%s' as `%s'.",
@@ -760,5 +770,5 @@
 	printf(NAME ": HelenOS Device Manager\n");
 
-	if (log_init(NAME, LVL_ERROR) != EOK) {
+	if (log_init(NAME, LVL_WARN) != EOK) {
 		printf(NAME ": Error initializing logging subsystem.\n");
 		return -1;
Index: uspace/srv/hid/input/ctl/kbdev.c
===================================================================
--- uspace/srv/hid/input/ctl/kbdev.c	(revision 86ffa27f45682aa2a5736f665f400ad262ce5baf)
+++ uspace/srv/hid/input/ctl/kbdev.c	(revision cc574511968ab8b7b5afe2cbb60c2053608cc393)
@@ -48,5 +48,7 @@
 #include <kbd_ctl.h>
 #include <kbd_port.h>
+#include <loc.h>
 #include <stdlib.h>
+#include <sys/typefmt.h>
 #include <vfs/vfs_sess.h>
 
@@ -70,7 +72,4 @@
 	/** Session with kbdev device */
 	async_sess_t *sess;
-
-	/** File descriptor of open kbdev device */
-	int fd;
 } kbdev_t;
 
@@ -84,5 +83,4 @@
 
 	kbdev->kbd_dev = kdev;
-	kbdev->fd = -1;
 
 	return kbdev;
@@ -93,6 +91,4 @@
 	if (kbdev->sess != NULL)
 		async_hangup(kbdev->sess);
-	if (kbdev->fd >= 0)
-		close(kbdev->fd);
 	free(kbdev);
 }
@@ -100,22 +96,17 @@
 static int kbdev_ctl_init(kbd_dev_t *kdev)
 {
-	const char *pathname;
 	async_sess_t *sess;
 	async_exch_t *exch;
 	kbdev_t *kbdev;
-	int fd;
+	char *svc_name;
 	int rc;
 
-	pathname = kdev->dev_path;
+	if (asprintf(&svc_name, "devname%" PRIun, kdev->service_id) > 0)
+		svc_name = (char *) "unknown";
 
-	fd = open(pathname, O_RDWR);
-	if (fd < 0) {
-		return -1;
-	}
-
-	sess = fd_session(EXCHANGE_SERIALIZE, fd);
+	sess = loc_service_connect(EXCHANGE_SERIALIZE, kdev->service_id, 0);
 	if (sess == NULL) {
-		printf("%s: Failed starting session with '%s'\n", NAME, pathname);
-		close(fd);
+		printf("%s: Failed starting session with '%s.'\n", NAME,
+		    svc_name);
 		return -1;
 	}
@@ -124,14 +115,14 @@
 	if (kbdev == NULL) {
 		printf("%s: Failed allocating device structure for '%s'.\n",
-		    NAME, pathname);
+		    NAME, svc_name);
 		return -1;
 	}
 
-	kbdev->fd = fd;
 	kbdev->sess = sess;
 
 	exch = async_exchange_begin(sess);
 	if (exch == NULL) {
-		printf("%s: Failed starting exchange with '%s'.\n", NAME, pathname);
+		printf("%s: Failed starting exchange with '%s'.\n", NAME,
+		    svc_name);
 		kbdev_destroy(kbdev);
 		return -1;
@@ -141,5 +132,5 @@
 	if (rc != EOK) {
 		printf("%s: Failed creating callback connection from '%s'.\n",
-		    NAME, pathname);
+		    NAME, svc_name);
 		async_exchange_end(exch);
 		kbdev_destroy(kbdev);
Index: uspace/srv/hid/input/generic/input.c
===================================================================
--- uspace/srv/hid/input/generic/input.c	(revision 86ffa27f45682aa2a5736f665f400ad262ce5baf)
+++ uspace/srv/hid/input/generic/input.c	(revision cc574511968ab8b7b5afe2cbb60c2053608cc393)
@@ -38,4 +38,5 @@
 
 #include <adt/list.h>
+#include <bool.h>
 #include <ipc/services.h>
 #include <ipc/input.h>
@@ -275,5 +276,5 @@
 	kdev->port_ops = port;
 	kdev->ctl_ops = ctl;
-	kdev->dev_path = NULL;
+	kdev->service_id = 0;
 	
 	/* Initialize port driver. */
@@ -303,5 +304,5 @@
 	mdev->port_ops = port;
 	mdev->proto_ops = proto;
-	mdev->dev_path = NULL;
+	mdev->service_id = 0;
 	
 	/* Initialize port driver. */
@@ -324,8 +325,8 @@
 /** Add new kbdev device.
  *
- * @param dev_path Filesystem path to the device (/loc/class/...)
+ * @param service_id	Service ID of the keyboard device
  *
  */
-static int kbd_add_kbdev(const char *dev_path)
+static int kbd_add_kbdev(service_id_t service_id)
 {
 	kbd_dev_t *kdev = kbd_dev_new();
@@ -333,5 +334,5 @@
 		return -1;
 	
-	kdev->dev_path = dev_path;
+	kdev->service_id = service_id;
 	kdev->port_ops = NULL;
 	kdev->ctl_ops = &kbdev_ctl;
@@ -352,8 +353,8 @@
 /** Add new mousedev device.
  *
- * @param dev_path Filesystem path to the device (/loc/class/...)
+ * @param service_id	Service ID of the mouse device
  *
  */
-static int mouse_add_mousedev(const char *dev_path)
+static int mouse_add_mousedev(service_id_t service_id)
 {
 	mouse_dev_t *mdev = mouse_dev_new();
@@ -361,5 +362,5 @@
 		return -1;
 	
-	mdev->dev_path = dev_path;
+	mdev->service_id = service_id;
 	mdev->port_ops = NULL;
 	mdev->proto_ops = &mousedev_proto;
@@ -487,10 +488,25 @@
  *
  */
+#include <sys/typefmt.h>
 static int dev_discovery_fibril(void *arg)
 {
-	char *dev_path;
-	size_t kbd_id = 1;
-	size_t mouse_id = 1;
+	category_id_t keyboard_cat, mouse_cat;
+	service_id_t *svcs;
+	size_t count, i;
+	bool already_known;
+	const char *dev_name = "todo";
 	int rc;
+	
+	rc = loc_category_get_id("keyboard", &keyboard_cat, IPC_FLAG_BLOCKING);
+	if (rc != EOK) {
+		printf("%s: Failed resolving category 'keyboard'.\n", NAME);
+		return ENOENT;
+	}
+	
+	rc = loc_category_get_id("mouse", &mouse_cat, IPC_FLAG_BLOCKING);
+	if (rc != EOK) {
+		printf("%s: Failed resolving category 'mouse'.\n", NAME);
+		return ENOENT;
+	}
 	
 	while (true) {
@@ -498,36 +514,68 @@
 		
 		/*
-		 * Check for new keyboard device
+		 * Check for new keyboard devices
 		 */
-		rc = asprintf(&dev_path, "/loc/class/keyboard\\%zu", kbd_id);
-		if (rc < 0)
+		rc = loc_category_get_svcs(keyboard_cat, &svcs, &count);
+		if (rc != EOK) {
+			printf("%s: Failed getting list of keyboard devices.\n",
+			    NAME);
 			continue;
-		
-		if (kbd_add_kbdev(dev_path) == EOK) {
-			printf("%s: Connected keyboard device '%s'\n",
-			    NAME, dev_path);
+		}
+
+		for (i = 0; i < count; i++) {
+			already_known = false;
 			
-			/* XXX Handle device removal */
-			++kbd_id;
+			/* Determine whether we already know this device. */
+			list_foreach(kbd_devs, kdev_link) {
+				kbd_dev_t *kdev = list_get_instance(kdev_link,
+				    kbd_dev_t, kbd_devs);
+				if (kdev->service_id == svcs[i]) {
+					already_known = true;
+					break;
+				}
+			}
+
+			if (!already_known) {
+				if (kbd_add_kbdev(svcs[i]) == EOK) {
+					printf("%s: Connected keyboard device '%s'\n",
+					    NAME, dev_name);
+				}
+			}
 		}
 		
-		free(dev_path);
+		/* XXX Handle device removal */
 		
 		/*
-		 * Check for new mouse device
+		 * Check for new mouse devices
 		 */
-		rc = asprintf(&dev_path, "/loc/class/mouse\\%zu", mouse_id);
-		if (rc < 0)
+		rc = loc_category_get_svcs(mouse_cat, &svcs, &count);
+		if (rc != EOK) {
+			printf("%s: Failed getting list of mouse devices.\n",
+			    NAME);
 			continue;
-		
-		if (mouse_add_mousedev(dev_path) == EOK) {
-			printf("%s: Connected mouse device '%s'\n",
-			    NAME, dev_path);
+		}
+
+		for (i = 0; i < count; i++) {
+			already_known = false;
 			
-			/* XXX Handle device removal */
-			++mouse_id;
+			/* Determine whether we already know this device. */
+			list_foreach(mouse_devs, mdev_link) {
+				mouse_dev_t *mdev = list_get_instance(mdev_link,
+				    mouse_dev_t, mouse_devs);
+				if (mdev->service_id == svcs[i]) {
+					already_known = true;
+					break;
+				}
+			}
+
+			if (!already_known) {
+				if (mouse_add_mousedev(svcs[i]) == EOK) {
+					printf("%s: Connected mouse device '%s'\n",
+					    NAME, dev_name);
+				}
+			}
 		}
 		
-		free(dev_path);
+		/* XXX Handle device removal */
 	}
 	
Index: uspace/srv/hid/input/include/kbd.h
===================================================================
--- uspace/srv/hid/input/include/kbd.h	(revision 86ffa27f45682aa2a5736f665f400ad262ce5baf)
+++ uspace/srv/hid/input/include/kbd.h	(revision cc574511968ab8b7b5afe2cbb60c2053608cc393)
@@ -40,4 +40,5 @@
 
 #include <adt/list.h>
+#include <ipc/loc.h>
 
 struct kbd_port_ops;
@@ -49,6 +50,6 @@
 	link_t kbd_devs;
 
-	/** Path to the device (only for kbdev devices) */
-	const char *dev_path;
+	/** Service ID (only for kbdev devices) */
+	service_id_t service_id;
 
 	/** Port ops */
Index: uspace/srv/hid/input/include/mouse.h
===================================================================
--- uspace/srv/hid/input/include/mouse.h	(revision 86ffa27f45682aa2a5736f665f400ad262ce5baf)
+++ uspace/srv/hid/input/include/mouse.h	(revision cc574511968ab8b7b5afe2cbb60c2053608cc393)
@@ -39,4 +39,5 @@
 
 #include <adt/list.h>
+#include <ipc/loc.h>
 
 struct mouse_port_ops;
@@ -47,6 +48,6 @@
 	link_t mouse_devs;
 	
-	/** Path to the device (only for mouseev devices) */
-	const char *dev_path;
+	/** Service ID (only for mousedev devices) */
+	service_id_t service_id;
 	
 	/** Port ops */
Index: uspace/srv/hid/input/proto/mousedev.c
===================================================================
--- uspace/srv/hid/input/proto/mousedev.c	(revision 86ffa27f45682aa2a5736f665f400ad262ce5baf)
+++ uspace/srv/hid/input/proto/mousedev.c	(revision cc574511968ab8b7b5afe2cbb60c2053608cc393)
@@ -44,7 +44,9 @@
 #include <ipc/mouseev.h>
 #include <input.h>
+#include <loc.h>
 #include <mouse.h>
 #include <mouse_port.h>
 #include <mouse_proto.h>
+#include <sys/typefmt.h>
 
 /** Mousedev softstate */
@@ -55,7 +57,4 @@
 	/** Session to mouse device */
 	async_sess_t *sess;
-	
-	/** File descriptor of open mousedev device */
-	int fd;
 } mousedev_t;
 
@@ -67,5 +66,4 @@
 	
 	mousedev->mouse_dev = mdev;
-	mousedev->fd = -1;
 	
 	return mousedev;
@@ -76,7 +74,4 @@
 	if (mousedev->sess != NULL)
 		async_hangup(mousedev->sess);
-	
-	if (mousedev->fd >= 0)
-		close(mousedev->fd);
 	
 	free(mousedev);
@@ -122,14 +117,13 @@
 static int mousedev_proto_init(mouse_dev_t *mdev)
 {
-	const char *pathname = mdev->dev_path;
+	char *svc_name;
 	
-	int fd = open(pathname, O_RDWR);
-	if (fd < 0)
-		return -1;
+	if (asprintf(&svc_name, "devname%" PRIun, mdev->service_id) > 0)
+		svc_name = (char *) "unknown";
 	
-	async_sess_t *sess = fd_session(EXCHANGE_SERIALIZE, fd);
+	async_sess_t *sess = loc_service_connect(EXCHANGE_SERIALIZE,
+	    mdev->service_id, 0);
 	if (sess == NULL) {
-		printf("%s: Failed starting session with '%s'\n", NAME, pathname);
-		close(fd);
+		printf("%s: Failed starting session with '%s'\n", NAME, svc_name);
 		return -1;
 	}
@@ -138,14 +132,14 @@
 	if (mousedev == NULL) {
 		printf("%s: Failed allocating device structure for '%s'.\n",
-		    NAME, pathname);
+		    NAME, svc_name);
 		return -1;
 	}
 	
-	mousedev->fd = fd;
 	mousedev->sess = sess;
 	
 	async_exch_t *exch = async_exchange_begin(sess);
 	if (exch == NULL) {
-		printf("%s: Failed starting exchange with '%s'.\n", NAME, pathname);
+		printf("%s: Failed starting exchange with '%s'.\n", NAME,
+		    svc_name);
 		mousedev_destroy(mousedev);
 		return -1;
@@ -157,5 +151,5 @@
 	if (rc != EOK) {
 		printf("%s: Failed creating callback connection from '%s'.\n",
-		    NAME, pathname);
+		    NAME, svc_name);
 		mousedev_destroy(mousedev);
 		return -1;
Index: uspace/srv/loc/Makefile
===================================================================
--- uspace/srv/loc/Makefile	(revision 86ffa27f45682aa2a5736f665f400ad262ce5baf)
+++ uspace/srv/loc/Makefile	(revision cc574511968ab8b7b5afe2cbb60c2053608cc393)
@@ -33,4 +33,5 @@
 
 SOURCES = \
+	category.c \
 	loc.c
 
Index: uspace/srv/loc/category.c
===================================================================
--- uspace/srv/loc/category.c	(revision cc574511968ab8b7b5afe2cbb60c2053608cc393)
+++ uspace/srv/loc/category.c	(revision cc574511968ab8b7b5afe2cbb60c2053608cc393)
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2011 Jiri Svoboda
+ * 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 loc
+ * @{
+ */
+/** @file Categories for location service.
+ */
+
+#include <adt/list.h>
+#include <errno.h>
+#include <fibril_synch.h>
+#include <stdlib.h>
+#include <str.h>
+
+#include "category.h"
+#include "loc.h"
+
+/** Initialize category directory. */
+void categ_dir_init(categ_dir_t *cdir)
+{
+	fibril_mutex_initialize(&cdir->mutex);
+	list_initialize(&cdir->categories);
+}
+
+/** Add new category to directory. */
+void categ_dir_add_cat(categ_dir_t *cdir, category_t *cat)
+{
+	list_append(&cat->cat_list, &cdir->categories);
+}
+
+/** Initialize category structure. */
+static void category_init(category_t *cat, const char *name)
+{
+	fibril_mutex_initialize(&cat->mutex);
+	cat->name = str_dup(name);
+	cat->id = loc_create_id();
+	link_initialize(&cat->cat_list);
+	list_initialize(&cat->services);
+}
+
+/** Allocate new category. */
+category_t *category_new(const char *name)
+{
+	category_t *cat;
+
+	cat = malloc(sizeof(category_t));
+	if (cat == NULL)
+		return NULL;
+
+	category_init(cat, name);
+	return cat;
+}
+
+/** Add service to category. */
+int category_add_service(category_t *cat, loc_service_t *svc)
+{
+	assert(fibril_mutex_is_locked(&cat->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) {
+			return EEXIST;
+		}
+	}
+
+	list_append(&svc->cat_services, &cat->services);
+	return EOK;
+}
+
+/** Get category by ID. */
+category_t *category_get(categ_dir_t *cdir, catid_t catid)
+{
+	assert(fibril_mutex_is_locked(&cdir->mutex));
+
+	list_foreach(cdir->categories, item) {
+		category_t *cat = list_get_instance(item, category_t,
+		    cat_list);
+		if (cat->id == catid)
+			return cat;
+	}
+
+	return NULL;
+}
+
+/** Find category by name. */
+category_t *category_find_by_name(categ_dir_t *cdir, const char *name)
+{
+	assert(fibril_mutex_is_locked(&cdir->mutex));
+
+	list_foreach(cdir->categories, item) {
+		category_t *cat = list_get_instance(item, category_t,
+		    cat_list);
+		if (str_cmp(cat->name, name) == 0)
+			return cat;
+	}
+
+	return NULL;
+}
+
+/** Get list of services in category. */
+int category_get_services(category_t *cat, service_id_t *id_buf,
+    size_t buf_size, size_t *act_size)
+{
+	size_t act_cnt;
+	size_t buf_cnt;
+
+	assert(fibril_mutex_is_locked(&cat->mutex));
+
+	buf_cnt = buf_size / sizeof(service_id_t);
+
+	act_cnt = list_count(&cat->services);
+	*act_size = act_cnt * sizeof(service_id_t);
+
+	if (buf_size % sizeof(service_id_t) != 0)
+		return EINVAL;
+
+	size_t pos = 0;
+	list_foreach(cat->services, item) {
+		loc_service_t *svc =
+		    list_get_instance(item, loc_service_t, cat_services);
+
+		if (pos < buf_cnt)
+			id_buf[pos] = svc->id;
+		pos++;
+	}
+
+	return EOK;
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/loc/category.h
===================================================================
--- uspace/srv/loc/category.h	(revision cc574511968ab8b7b5afe2cbb60c2053608cc393)
+++ uspace/srv/loc/category.h	(revision cc574511968ab8b7b5afe2cbb60c2053608cc393)
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2011 Jiri Svoboda
+ * 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 loc
+ * @{
+ */
+/** @file Categories for location service.
+ */
+
+#ifndef CATEGORY_H_
+#define CATEGORY_H_
+
+#include <adt/list.h>
+#include "loc.h"
+
+typedef sysarg_t catid_t;
+
+/** Service category */
+typedef struct {
+	/** Protects this structure, list of services */
+	fibril_mutex_t mutex;
+
+	/** Identifier */
+	catid_t id;
+
+	/** Category name */
+	const char *name;
+
+	/** Link to list of categories (categ_dir_t.categories) */
+	link_t cat_list;
+
+	/** List of services in this category (loc_service_t) */
+	list_t services;
+} category_t;
+
+/** Service directory ogranized by categories (yellow pages) */
+typedef struct {
+	/** Protects this structure, list of categories */
+	fibril_mutex_t mutex;
+	/** List of all categories (category_t) */
+	list_t categories;
+} categ_dir_t;
+
+extern void categ_dir_init(categ_dir_t *);
+extern void categ_dir_add_cat(categ_dir_t *, category_t *);
+extern category_t *category_new(const char *);
+extern int category_add_service(category_t *, loc_service_t *);
+extern category_t *category_get(categ_dir_t *, catid_t);
+extern category_t *category_find_by_name(categ_dir_t *, const char *);
+extern int category_get_services(category_t *, service_id_t *, size_t,
+    size_t *);
+
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/loc/loc.c
===================================================================
--- uspace/srv/loc/loc.c	(revision 86ffa27f45682aa2a5736f665f400ad262ce5baf)
+++ uspace/srv/loc/loc.c	(revision cc574511968ab8b7b5afe2cbb60c2053608cc393)
@@ -49,65 +49,9 @@
 #include <assert.h>
 
+#include "category.h"
+#include "loc.h"
+
 #define NAME          "loc"
 #define NULL_SERVICES  256
-
-/** Representation of server (supplier).
- *
- * Each server supplies a set of services.
- *
- */
-typedef struct {
-	/** Link to servers_list */
-	link_t servers;
-	
-	/** List of services supplied by this server */
-	list_t services;
-	
-	/** Session asociated with this server */
-	async_sess_t *sess;
-	
-	/** Server name */
-	char *name;
-	
-	/** Fibril mutex for list of services owned by this server */
-	fibril_mutex_t services_mutex;
-} loc_server_t;
-
-/** Info about registered namespaces
- *
- */
-typedef struct {
-	/** Link to namespaces_list */
-	link_t namespaces;
-	
-	/** Unique namespace identifier */
-	service_id_t id;
-	
-	/** Namespace name */
-	char *name;
-	
-	/** Reference count */
-	size_t refcnt;
-} loc_namespace_t;
-
-/** Info about registered service
- *
- */
-typedef struct {
-	/** Link to global list of services (services_list) */
-	link_t services;
-	/** Link to server list of services (loc_server_t.services) */
-	link_t server_services;
-	/** Unique service identifier */
-	service_id_t id;
-	/** Service namespace */
-	loc_namespace_t *namespace;
-	/** Service name */
-	char *name;
-	/** Supplier of this service */
-	loc_server_t *server;
-	/** Use this interface when forwarding to server. */
-	sysarg_t forward_interface;
-} loc_service_t;
 
 LIST_INITIALIZE(services_list);
@@ -137,5 +81,8 @@
 static LIST_INITIALIZE(dummy_null_services);
 
-static service_id_t loc_create_id(void)
+/** Service directory ogranized by categories (yellow pages) */
+static categ_dir_t cdir;
+
+service_id_t loc_create_id(void)
 {
 	/* TODO: allow reusing old ids after their unregistration
@@ -734,4 +681,38 @@
 }
 
+/** Find ID for category specified by name.
+ *
+ * On success, answer will contain EOK int retval and service ID in arg1.
+ * On failure, error code will be sent in retval.
+ *
+ */
+static void loc_category_get_id(ipc_callid_t iid, ipc_call_t *icall)
+{
+	char *name;
+	category_t *cat;
+	
+	/* Get service name */
+	int rc = async_data_write_accept((void **) &name, true, 0,
+	    LOC_NAME_MAXLEN, 0, NULL);
+	if (rc != EOK) {
+		async_answer_0(iid, rc);
+		return;
+	}
+	
+	fibril_mutex_lock(&cdir.mutex);
+
+	cat = category_find_by_name(&cdir, name);
+	if (cat == NULL) {
+		/* Category not found */
+		async_answer_0(iid, ENOENT);
+		goto cleanup;
+	}
+	
+	async_answer_1(iid, EOK, cat->id);
+cleanup:
+	fibril_mutex_unlock(&cdir.mutex);
+	free(name);
+}
+
 static void loc_id_probe(ipc_callid_t iid, ipc_call_t *icall)
 {
@@ -892,4 +873,56 @@
 }
 
+static void loc_category_get_svcs(ipc_callid_t iid, ipc_call_t *icall)
+{
+	ipc_callid_t callid;
+	size_t size;
+	size_t act_size;
+	int rc;
+	
+	if (!async_data_read_receive(&callid, &size)) {
+		async_answer_0(callid, EREFUSED);
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+	
+	fibril_mutex_lock(&cdir.mutex);
+	
+	category_t *cat = category_get(&cdir, IPC_GET_ARG1(*icall));
+	if (cat == NULL) {
+		fibril_mutex_unlock(&cdir.mutex);
+		async_answer_0(callid, ENOENT);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	category_id_t *id_buf = (category_id_t *) malloc(size);
+	if (id_buf == NULL) {
+		fibril_mutex_unlock(&cdir.mutex);
+		async_answer_0(callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+	
+	fibril_mutex_lock(&cat->mutex);
+	
+	rc = category_get_services(cat, id_buf, size, &act_size);
+	if (rc != EOK) {
+		fibril_mutex_unlock(&cat->mutex);
+		fibril_mutex_unlock(&cdir.mutex);
+		async_answer_0(callid, rc);
+		async_answer_0(iid, rc);
+		return;
+	}
+	
+	fibril_mutex_unlock(&cat->mutex);
+	fibril_mutex_unlock(&cdir.mutex);
+	
+	sysarg_t retval = async_data_read_finalize(callid, id_buf, size);
+	free(id_buf);
+	
+	async_answer_1(iid, retval, act_size);
+}
+
+
 static void loc_null_create(ipc_callid_t iid, ipc_call_t *icall)
 {
@@ -991,4 +1024,32 @@
 }
 
+static void loc_service_add_to_cat(ipc_callid_t iid, ipc_call_t *icall)
+{
+	category_t *cat;
+	loc_service_t *svc;
+	catid_t cat_id;
+	service_id_t svc_id;
+	sysarg_t retval;
+	
+	svc_id = IPC_GET_ARG1(*icall);
+	cat_id = IPC_GET_ARG2(*icall);
+	
+	fibril_mutex_lock(&services_list_mutex);
+	fibril_mutex_lock(&cdir.mutex);
+	
+	cat = category_get(&cdir, cat_id);
+	svc = loc_service_find_id(svc_id);
+	
+	fibril_mutex_lock(&cat->mutex);
+	retval = category_add_service(cat, svc);
+
+	fibril_mutex_unlock(&cat->mutex);
+	fibril_mutex_unlock(&cdir.mutex);
+	fibril_mutex_unlock(&services_list_mutex);
+
+	async_answer_0(iid, retval);
+}
+
+
 /** Initialize location service.
  *
@@ -997,12 +1058,24 @@
 static bool loc_init(void)
 {
-	fibril_mutex_lock(&null_services_mutex);
-	
 	unsigned int i;
+	category_t *cat;
+
 	for (i = 0; i < NULL_SERVICES; i++)
 		null_services[i] = NULL;
 	
-	fibril_mutex_unlock(&null_services_mutex);
-	
+	categ_dir_init(&cdir);
+
+	cat = category_new("bd");
+	categ_dir_add_cat(&cdir, cat);
+
+	cat = category_new("keyboard");
+	categ_dir_add_cat(&cdir, cat);
+
+	cat = category_new("mouse");
+	categ_dir_add_cat(&cdir, cat);
+
+	cat = category_new("serial");
+	categ_dir_add_cat(&cdir, cat);
+
 	return true;
 }
@@ -1034,4 +1107,8 @@
 				async_answer_0(callid, EOK);
 			break;
+		case LOC_SERVICE_ADD_TO_CAT:
+			/* Add service to category */
+			loc_service_add_to_cat(callid, &call);
+			break;
 		case LOC_SERVICE_REGISTER:
 			/* Register one service */
@@ -1083,4 +1160,10 @@
 		case LOC_NAMESPACE_GET_ID:
 			loc_namespace_get_id(callid, &call);
+			break;
+		case LOC_CATEGORY_GET_ID:
+			loc_category_get_id(callid, &call);
+			break;
+		case LOC_CATEGORY_GET_SVCS:
+			loc_category_get_svcs(callid, &call);
 			break;
 		case LOC_ID_PROBE:
Index: uspace/srv/loc/loc.h
===================================================================
--- uspace/srv/loc/loc.h	(revision cc574511968ab8b7b5afe2cbb60c2053608cc393)
+++ uspace/srv/loc/loc.h	(revision cc574511968ab8b7b5afe2cbb60c2053608cc393)
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2011 Jiri Svoboda
+ * 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 loc
+ * @{
+ */
+/** @file HelenOS location service.
+ */
+
+#ifndef LOC_H_
+#define LOC_H_
+
+#include <async.h>
+#include <fibril_synch.h>
+#include <sys/types.h>
+
+/** Representation of server (supplier).
+ *
+ * Each server supplies a set of services.
+ *
+ */
+typedef struct {
+	/** Link to servers_list */
+	link_t servers;
+	
+	/** List of services supplied by this server */
+	list_t services;
+	
+	/** Session asociated with this server */
+	async_sess_t *sess;
+	
+	/** Server name */
+	char *name;
+	
+	/** Fibril mutex for list of services owned by this server */
+	fibril_mutex_t services_mutex;
+} loc_server_t;
+
+/** Info about registered namespaces
+ *
+ */
+typedef struct {
+	/** Link to namespaces_list */
+	link_t namespaces;
+	
+	/** Unique namespace identifier */
+	service_id_t id;
+	
+	/** Namespace name */
+	char *name;
+	
+	/** Reference count */
+	size_t refcnt;
+} loc_namespace_t;
+
+/** Info about registered service
+ *
+ */
+typedef struct {
+	/** Link to global list of services (services_list) */
+	link_t services;
+	/** Link to server list of services (loc_server_t.services) */
+	link_t server_services;
+	/** Link to list of services in category (category_t.services) */
+	link_t cat_services;
+	/** Unique service identifier */
+	service_id_t id;
+	/** Service namespace */
+	loc_namespace_t *namespace;
+	/** Service name */
+	char *name;
+	/** Supplier of this service */
+	loc_server_t *server;
+	/** Use this interface when forwarding to server. */
+	sysarg_t forward_interface;
+} loc_service_t;
+
+extern service_id_t loc_create_id(void);
+
+#endif
+
+/** @}
+ */
