Index: uspace/srv/devman/main.c
===================================================================
--- uspace/srv/devman/main.c	(revision 17279ead2f144a2631841fc11794d550970a9466)
+++ uspace/srv/devman/main.c	(revision a0a134bd909bba4402830c91ca2e0b0fd1f41058)
@@ -595,5 +595,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 17279ead2f144a2631841fc11794d550970a9466)
+++ uspace/srv/fs/fat/fat_dentry.c	(revision a0a134bd909bba4402830c91ca2e0b0fd1f41058)
@@ -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 17279ead2f144a2631841fc11794d550970a9466)
+++ uspace/srv/hid/console/console.c	(revision a0a134bd909bba4402830c91ca2e0b0fd1f41058)
@@ -41,4 +41,5 @@
 #include <ipc/ns.h>
 #include <errno.h>
+#include <str_error.h>
 #include <ipc/console.h>
 #include <unistd.h>
@@ -64,4 +65,6 @@
 #define NAME       "console"
 #define NAMESPACE  "term"
+/** Interval for checking for new keyboard (1/4s). */
+#define HOTPLUG_WATCH_INTERVAL (1000 * 250)
 
 /** Phone to the keyboard driver. */
@@ -317,6 +320,7 @@
 static void change_console(console_t *cons)
 {
-	if (cons == active_console)
+	if (cons == active_console) {
 		return;
+	}
 	
 	fb_pending_flush();
@@ -458,6 +462,7 @@
 			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 +715,123 @@
 }
 
+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);
+	if (phone < 0) {
+		printf(NAME ": Failed to connect to input device\n");
+		return phone;
+	}
+	
+	int rc = async_connect_to_me(phone, SERVICE_CONSOLE, 0, 0, handler);
+	if (rc != EOK) {
+		printf(NAME ": " \
+		    "Failed to create callback from input device: %s.\n",
+		    str_error(rc));
+		return rc;
+	}
+	
+	printf(NAME ": found %s \"%s\".\n", devname, path);
+
+	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 +916,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 +939,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 17279ead2f144a2631841fc11794d550970a9466)
+++ uspace/srv/hw/irc/apic/apic.c	(revision a0a134bd909bba4402830c91ca2e0b0fd1f41058)
@@ -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 17279ead2f144a2631841fc11794d550970a9466)
+++ uspace/srv/hw/irc/i8259/i8259.c	(revision a0a134bd909bba4402830c91ca2e0b0fd1f41058)
@@ -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);
