Index: uspace/srv/devman/main.c
===================================================================
--- uspace/srv/devman/main.c	(revision e8d5f9f01c7476f0f5fa23365dfe29d30d758eda)
+++ uspace/srv/devman/main.c	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
@@ -515,4 +515,41 @@
 }
 
+/** Find device path by its handle. */
+static void devman_get_device_path_by_handle(ipc_callid_t iid,
+    ipc_call_t *icall)
+{
+	devman_handle_t handle = IPC_GET_ARG1(*icall);
+
+	fun_node_t *fun = find_fun_node(&device_tree, handle);
+	if (fun == NULL) {
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+
+	ipc_callid_t data_callid;
+	size_t data_len;
+	if (!async_data_read_receive(&data_callid, &data_len)) {
+		async_answer_0(iid, EINVAL);
+		return;
+	}
+
+	void *buffer = malloc(data_len);
+	if (buffer == NULL) {
+		async_answer_0(data_callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+
+	size_t sent_length = str_size(fun->pathname);
+	if (sent_length > data_len) {
+		sent_length = data_len;
+	}
+
+	async_data_read_finalize(data_callid, fun->pathname, sent_length);
+	async_answer_0(iid, EOK);
+
+	free(buffer);
+}
+
 
 /** Function for handling connections from a client to the device manager. */
@@ -537,4 +574,7 @@
 			devman_function_get_handle_by_class(callid, &call);
 			break;
+		case DEVMAN_DEVICE_GET_DEVICE_PATH:
+			devman_get_device_path_by_handle(callid, &call);
+			break;
 		default:
 			async_answer_0(callid, ENOENT);
@@ -595,5 +635,6 @@
 	if (driver == NULL) {
 		log_msg(LVL_ERROR, "IPC forwarding refused - " \
-		    "the device %" PRIun " is not in usable state.", handle);
+		    "the device %" PRIun "(%s) is not in usable state.",
+		    handle, dev->pfun->pathname);
 		async_answer_0(iid, ENOENT);
 		return;
Index: uspace/srv/fs/fat/fat_dentry.c
===================================================================
--- uspace/srv/fs/fat/fat_dentry.c	(revision e8d5f9f01c7476f0f5fa23365dfe29d30d758eda)
+++ uspace/srv/fs/fat/fat_dentry.c	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
@@ -42,5 +42,5 @@
 static bool is_d_char(const char ch)
 {
-	if (isalnum(ch) || ch == '_')
+	if (isalnum(ch) || ch == '_' || ch == '-')
 		return true;
 	else
Index: uspace/srv/hid/console/console.c
===================================================================
--- uspace/srv/hid/console/console.c	(revision e8d5f9f01c7476f0f5fa23365dfe29d30d758eda)
+++ uspace/srv/hid/console/console.c	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
@@ -41,4 +41,5 @@
 #include <ipc/ns.h>
 #include <errno.h>
+#include <str_error.h>
 #include <ipc/console.h>
 #include <unistd.h>
@@ -56,4 +57,5 @@
 #include <io/style.h>
 #include <io/screenbuffer.h>
+#include <inttypes.h>
 
 #include "console.h"
@@ -64,4 +66,11 @@
 #define NAME       "console"
 #define NAMESPACE  "term"
+/** Interval for checking for new keyboard (1/4s). */
+#define HOTPLUG_WATCH_INTERVAL (1000 * 250)
+
+/* Kernel defines 32 but does not export it. */
+#define MAX_IPC_OUTGOING_PHONES 128
+/** To allow proper phone closing. */
+static ipc_callid_t driver_phones[MAX_IPC_OUTGOING_PHONES] = { 0 };
 
 /** Phone to the keyboard driver. */
@@ -88,4 +97,6 @@
 } console_t;
 
+
+
 /** Array of data for virtual consoles */
 static console_t consoles[CONSOLE_COUNT];
@@ -317,6 +328,7 @@
 static void change_console(console_t *cons)
 {
-	if (cons == active_console)
+	if (cons == active_console) {
 		return;
+	}
 	
 	fb_pending_flush();
@@ -397,4 +409,17 @@
 }
 
+static void close_driver_phone(ipc_callid_t hash)
+{
+	int i;
+	for (i = 0; i < MAX_IPC_OUTGOING_PHONES; i++) {
+		if (driver_phones[i] == hash) {
+			printf("Device %" PRIxn " gone.\n", hash);
+			driver_phones[i] = 0;
+			async_hangup(i);
+			return;
+		}
+	}
+}
+
 /** Handler for keyboard */
 static void keyboard_events(ipc_callid_t iid, ipc_call_t *icall)
@@ -411,4 +436,5 @@
 		case IPC_M_PHONE_HUNGUP:
 			/* TODO: Handle hangup */
+			close_driver_phone(iid);
 			return;
 		case KBD_EVENT:
@@ -454,10 +480,12 @@
 		case IPC_M_PHONE_HUNGUP:
 			/* TODO: Handle hangup */
+			close_driver_phone(iid);
 			return;
 		case MEVENT_BUTTON:
 			if (IPC_GET_ARG1(call) == 1) {
 				int newcon = gcons_mouse_btn((bool) IPC_GET_ARG2(call));
-				if (newcon != -1)
+				if (newcon != -1) {
 					change_console(&consoles[newcon]);
+				}
 			}
 			retval = 0;
@@ -710,49 +738,150 @@
 }
 
+static int async_connect_to_me_hack(int phone, sysarg_t arg1, sysarg_t arg2,
+sysarg_t arg3, async_client_conn_t client_receiver, ipc_callid_t *hash)
+{
+	sysarg_t task_hash;
+	sysarg_t phone_hash;
+	int rc = async_req_3_5(phone, IPC_M_CONNECT_TO_ME, arg1, arg2, arg3,
+	    NULL, NULL, NULL, &task_hash, &phone_hash);
+	if (rc != EOK)
+		return rc;
+
+	if (client_receiver != NULL)
+		async_new_connection(task_hash, phone_hash, phone_hash, NULL,
+		    client_receiver);
+
+	if (hash != NULL) {
+		*hash = phone_hash;
+	}
+
+	return EOK;
+}
+
+static int connect_keyboard_or_mouse(const char *devname,
+    async_client_conn_t handler, const char *path)
+{
+	int fd = open(path, O_RDONLY);
+	if (fd < 0) {
+		return fd;
+	}
+	
+	int phone = fd_phone(fd);
+	close(fd);
+	if (phone < 0) {
+		printf(NAME ": Failed to connect to input device\n");
+		return phone;
+	}
+	
+	ipc_callid_t hash;
+	int rc = async_connect_to_me_hack(phone, SERVICE_CONSOLE, 0, phone,
+	    handler, &hash);
+	if (rc != EOK) {
+		async_hangup(phone);
+		printf(NAME ": " \
+		    "Failed to create callback from input device: %s.\n",
+		    str_error(rc));
+		return rc;
+	}
+	
+	driver_phones[phone] = hash;
+
+	printf(NAME ": found %s \"%s\" (%" PRIxn ").\n", devname, path, hash);
+
+	return phone;
+}
+
+static int connect_keyboard(const char *path)
+{
+	return connect_keyboard_or_mouse("keyboard", keyboard_events, path);
+}
+
+static int connect_mouse(const char *path)
+{
+	return connect_keyboard_or_mouse("mouse", mouse_events, path);
+}
+
+struct hid_class_info {
+	char *classname;
+	int (*connection_func)(const char *);
+};
+
+/** Periodically check for new keyboards in /dev/class/.
+ *
+ * @param arg Class name.
+ * @return This function should never exit.
+ */
+static int check_new_device_fibril(void *arg)
+{
+	struct hid_class_info *dev_info = arg;
+
+	size_t index = 1;
+
+	while (true) {
+		async_usleep(HOTPLUG_WATCH_INTERVAL);
+		char *path;
+		int rc = asprintf(&path, "/dev/class/%s\\%zu",
+		    dev_info->classname, index);
+		if (rc < 0) {
+			continue;
+		}
+		rc = 0;
+		rc = dev_info->connection_func(path);
+		if (rc > 0) {
+			/* We do not allow unplug. */
+			index++;
+		}
+
+		free(path);
+	}
+
+	return EOK;
+}
+
+
+/** Start a fibril monitoring hot-plugged keyboards.
+ */
+static void check_new_devices_in_background(int (*connection_func)(const char *),
+    const char *classname)
+{
+	struct hid_class_info *dev_info = malloc(sizeof(struct hid_class_info));
+	if (dev_info == NULL) {
+		printf(NAME ": " \
+		    "out of memory, will not start hot-plug-watch fibril.\n");
+		return;
+	}
+	int rc;
+
+	rc = asprintf(&dev_info->classname, "%s", classname);
+	if (rc < 0) {
+		printf(NAME ": failed to format classname: %s.\n",
+		    str_error(rc));
+		return;
+	}
+	dev_info->connection_func = connection_func;
+
+	fid_t fid = fibril_create(check_new_device_fibril, (void *)dev_info);
+	if (!fid) {
+		printf(NAME
+		    ": failed to create hot-plug-watch fibril for %s.\n",
+		    classname);
+		return;
+	}
+	fibril_add_ready(fid);
+}
+
 static bool console_init(char *input)
 {
 	/* Connect to input device */
-	int input_fd = open(input, O_RDONLY);
-	if (input_fd < 0) {
-		printf(NAME ": Failed opening %s\n", input);
+	kbd_phone = connect_keyboard(input);
+	if (kbd_phone < 0) {
 		return false;
 	}
-	
-	kbd_phone = fd_phone(input_fd);
-	if (kbd_phone < 0) {
-		printf(NAME ": Failed to connect to input device\n");
-		return false;
-	}
-	
-	/* NB: The callback connection is slotted for removal */
-	if (async_connect_to_me(kbd_phone, SERVICE_CONSOLE, 0, 0, keyboard_events)
-	    != 0) {
-		printf(NAME ": Failed to create callback from input device\n");
-		return false;
-	}
-	
-	/* Connect to mouse device */
-	mouse_phone = -1;
-	int mouse_fd = open("/dev/hid_in/mouse", O_RDONLY);
-	
-	if (mouse_fd < 0) {
-		printf(NAME ": Notice - failed opening %s\n", "/dev/hid_in/mouse");
-		goto skip_mouse;
-	}
-	
-	mouse_phone = fd_phone(mouse_fd);
+
+	mouse_phone = connect_mouse("/dev/hid_in/mouse");
 	if (mouse_phone < 0) {
-		printf(NAME ": Failed to connect to mouse device\n");
-		goto skip_mouse;
-	}
-	
-	if (async_connect_to_me(mouse_phone, SERVICE_CONSOLE, 0, 0, mouse_events)
-	    != 0) {
-		printf(NAME ": Failed to create callback from mouse device\n");
-		mouse_phone = -1;
-		goto skip_mouse;
-	}
-	
-skip_mouse:
+		printf(NAME ": Failed to connect to mouse device: %s.\n",
+		    str_error(mouse_phone));
+	}
 	
 	/* Connect to framebuffer driver */
@@ -837,4 +966,8 @@
 		printf(NAME ": Error registering kconsole notifications\n");
 	
+	/* Start fibril for checking on hot-plugged keyboards. */
+	check_new_devices_in_background(connect_keyboard, "keyboard");
+	check_new_devices_in_background(connect_mouse, "mouse");
+
 	return true;
 }
@@ -856,5 +989,5 @@
 	if (!console_init(argv[1]))
 		return -1;
-	
+
 	printf(NAME ": Accepting connections\n");
 	async_manager();
Index: uspace/srv/hw/irc/apic/apic.c
===================================================================
--- uspace/srv/hw/irc/apic/apic.c	(revision e8d5f9f01c7476f0f5fa23365dfe29d30d758eda)
+++ uspace/srv/hw/irc/apic/apic.c	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
@@ -87,4 +87,8 @@
 			async_answer_0(callid, EOK);
 			break;
+		case IPC_M_PHONE_HUNGUP:
+			/* The other side has hung up. */
+			async_answer_0(callid, EOK);
+			return;
 		default:
 			async_answer_0(callid, EINVAL);
Index: uspace/srv/hw/irc/i8259/i8259.c
===================================================================
--- uspace/srv/hw/irc/i8259/i8259.c	(revision e8d5f9f01c7476f0f5fa23365dfe29d30d758eda)
+++ uspace/srv/hw/irc/i8259/i8259.c	(revision 083adbcf45e7e4bc4233cdfd9ba33fb0d8dd78f9)
@@ -121,4 +121,8 @@
 			async_answer_0(callid, EOK);
 			break;
+		case IPC_M_PHONE_HUNGUP:
+			/* The other side has hung up. */
+			async_answer_0(callid, EOK);
+			return;
 		default:
 			async_answer_0(callid, EINVAL);
