Index: uspace/srv/devman/devman.c
===================================================================
--- uspace/srv/devman/devman.c	(revision b38dfd8028d6cec3849c0eccfe3dff997f218265)
+++ uspace/srv/devman/devman.c	(revision 4006447f76b2458440272197d0fda72d4caecbe8)
@@ -508,6 +508,4 @@
 /** Notify driver about the devices to which it was assigned.
  *
- * The driver's mutex must be locked.
- *
  * @param driver	The driver to which the devices are passed.
  */
@@ -518,18 +516,75 @@
 	int phone;
 
-	printf(NAME ": pass_devices_to_driver\n");
-
-	phone = ipc_connect_me_to(driver->phone, DRIVER_DEVMAN, 0, 0);
-	if (phone > 0) {
-		
+	printf(NAME ": pass_devices_to_driver(`%s')\n", driver->name);
+
+	fibril_mutex_lock(&driver->driver_mutex);
+
+	phone = async_connect_me_to(driver->phone, DRIVER_DEVMAN, 0, 0);
+
+	if (phone < 0) {
+		fibril_mutex_unlock(&driver->driver_mutex);
+		return;
+	}
+
+	/*
+	 * Go through devices list as long as there is some device
+	 * that has not been passed to the driver.
+	 */
+	link = driver->devices.next;
+	while (link != &driver->devices) {
+		dev = list_get_instance(link, node_t, driver_devices);
+		if (dev->passed_to_driver) {
+			link = link->next;
+			continue;
+		}
+
+		/*
+		 * We remove the device from the list to allow safe adding
+		 * of new devices (no one will touch our item this way).
+		 */
+		list_remove(link);
+
+		/*
+		 * Unlock to avoid deadlock when adding device
+		 * handled by itself.
+		 */
+		fibril_mutex_unlock(&driver->driver_mutex);
+
+		add_device(phone, driver, dev, tree);
+
+		/*
+		 * Lock again as we will work with driver's
+		 * structure.
+		 */
+		fibril_mutex_lock(&driver->driver_mutex);
+
+		/*
+		 * Insert the device back.
+		 * The order is not relevant here so no harm is done
+		 * (actually, the order would be preserved in most cases).
+		 */
+		list_append(link, &driver->devices);
+
+		/*
+		 * Restart the cycle to go through all devices again.
+		 */
 		link = driver->devices.next;
-		while (link != &driver->devices) {
-			dev = list_get_instance(link, node_t, driver_devices);
-			add_device(phone, driver, dev, tree);
-			link = link->next;
-		}
-		
-		ipc_hangup(phone);
-	}
+	}
+
+	ipc_hangup(phone);
+
+	/*
+	 * Once we passed all devices to the driver, we need to mark the
+	 * driver as running.
+	 * It is vital to do it here and inside critical section.
+	 *
+	 * If we would change the state earlier, other devices added to
+	 * the driver would be added to the device list and started
+	 * immediately and possibly started here as well.
+	 */
+	printf(NAME ": driver %s goes into running state.\n", driver->name);
+	driver->state = DRIVER_RUNNING;
+
+	fibril_mutex_unlock(&driver->driver_mutex);
 }
 
@@ -545,6 +600,5 @@
 void initialize_running_driver(driver_t *driver, dev_tree_t *tree)
 {
-	printf(NAME ": initialize_running_driver\n");
-	fibril_mutex_lock(&driver->driver_mutex);
+	printf(NAME ": initialize_running_driver (`%s')\n", driver->name);
 	
 	/*
@@ -553,9 +607,4 @@
 	 */
 	pass_devices_to_driver(driver, tree);
-	
-	/* Change driver's state to running. */
-	driver->state = DRIVER_RUNNING;
-	
-	fibril_mutex_unlock(&driver->driver_mutex);
 }
 
@@ -629,4 +678,5 @@
 }
 
+static FIBRIL_MUTEX_INITIALIZE(add_device_guard);
 
 /** Pass a device to running driver.
@@ -637,5 +687,12 @@
 void add_device(int phone, driver_t *drv, node_t *node, dev_tree_t *tree)
 {
-	printf(NAME ": add_device\n");
+	fibril_mutex_lock(&add_device_guard);
+
+	/*
+	 * We do not expect to have driver's mutex locked as we do not
+	 * access any structures that would affect driver_t.
+	 */
+	printf(NAME ": add_device (driver `%s', device `%s')\n", drv->name,
+	    node->name);
 	
 	ipcarg_t rc;
@@ -643,6 +700,13 @@
 	
 	/* Send the device to the driver. */
-	aid_t req = async_send_1(phone, DRIVER_ADD_DEVICE, node->handle,
-	    &answer);
+	devman_handle_t parent_handle;
+	if (node->parent) {
+		parent_handle = node->parent->handle;
+	} else {
+		parent_handle = 0;
+	}
+
+	aid_t req = async_send_2(phone, DRIVER_ADD_DEVICE, node->handle,
+	    parent_handle, &answer);
 	
 	/* Send the device's name to the driver. */
@@ -652,7 +716,10 @@
 		/* TODO handle error */
 	}
-	
+
 	/* Wait for answer from the driver. */
 	async_wait_for(req, &rc);
+
+	fibril_mutex_unlock(&add_device_guard);
+
 	switch(rc) {
 	case EOK:
@@ -667,4 +734,6 @@
 	}
 	
+	node->passed_to_driver = true;
+
 	return;
 }
@@ -692,12 +761,15 @@
 	attach_driver(node, drv);
 	
+	fibril_mutex_lock(&drv->driver_mutex);
 	if (drv->state == DRIVER_NOT_STARTED) {
 		/* Start the driver. */
 		start_driver(drv);
 	}
-	
-	if (drv->state == DRIVER_RUNNING) {
+	bool is_running = drv->state == DRIVER_RUNNING;
+	fibril_mutex_unlock(&drv->driver_mutex);
+
+	if (is_running) {
 		/* Notify the driver about the new device. */
-		int phone = ipc_connect_me_to(drv->phone, DRIVER_DEVMAN, 0, 0);
+		int phone = async_connect_me_to(drv->phone, DRIVER_DEVMAN, 0, 0);
 		if (phone > 0) {
 			add_device(phone, drv, node, tree);
@@ -861,5 +933,4 @@
 	node->name = dev_name;
 	if (!set_dev_path(node, parent)) {
-		fibril_rwlock_write_unlock(&tree->rwlock);
 		return false;
 	}
@@ -1083,6 +1154,8 @@
 	while (link != &class_list->classes) {
 		cl = list_get_instance(link, dev_class_t, link);
-		if (str_cmp(cl->name, class_name) == 0)
+		if (str_cmp(cl->name, class_name) == 0) {
 			return cl;
+		}
+		link = link->next;
 	}
 	
Index: uspace/srv/devman/devman.h
===================================================================
--- uspace/srv/devman/devman.h	(revision b38dfd8028d6cec3849c0eccfe3dff997f218265)
+++ uspace/srv/devman/devman.h	(revision 4006447f76b2458440272197d0fda72d4caecbe8)
@@ -168,4 +168,9 @@
 	 */
 	link_t devmap_link;
+
+	/**
+	 * Whether this device was already passed to the driver.
+	 */
+	bool passed_to_driver;
 };
 
Index: uspace/srv/devman/main.c
===================================================================
--- uspace/srv/devman/main.c	(revision b38dfd8028d6cec3849c0eccfe3dff997f218265)
+++ uspace/srv/devman/main.c	(revision 4006447f76b2458440272197d0fda72d4caecbe8)
@@ -197,4 +197,11 @@
 }
 
+static int assign_driver_fibril(void *arg)
+{
+	node_t *node = (node_t *) arg;
+	assign_driver(node, &drivers_list, &device_tree);
+	return EOK;
+}
+
 /** Handle child device registration.
  *
@@ -237,10 +244,25 @@
 	
 	devman_receive_match_ids(match_count, &node->match_ids);
-	
+
+	/*
+	 * Try to find a suitable driver and assign it to the device.  We do
+	 * not want to block the current fibril that is used for processing
+	 * incoming calls: we will launch a separate fibril to handle the
+	 * driver assigning. That is because assign_driver can actually include
+	 * task spawning which could take some time.
+	 */
+	fid_t assign_fibril = fibril_create(assign_driver_fibril, node);
+	if (assign_fibril == 0) {
+		/*
+		 * Fallback in case we are out of memory.
+		 * Probably not needed as we will die soon anyway ;-).
+		 */
+		(void) assign_driver_fibril(node);
+	} else {
+		fibril_add_ready(assign_fibril);
+	}
+
 	/* Return device handle to parent's driver. */
 	ipc_answer_1(callid, EOK, node->handle);
-	
-	/* Try to find suitable driver and assign it to the device. */
-	assign_driver(node, &drivers_list, &device_tree);
 }
 
@@ -297,5 +319,5 @@
 	printf(NAME ": device '%s' added to class '%s', class name '%s' was "
 	    "asigned to it\n", dev->pathname, class_name, class_info->dev_name);
-	
+
 	ipc_answer_0(callid, EOK);
 }
Index: uspace/srv/devman/match.c
===================================================================
--- uspace/srv/devman/match.c	(revision b38dfd8028d6cec3849c0eccfe3dff997f218265)
+++ uspace/srv/devman/match.c	(revision 4006447f76b2458440272197d0fda72d4caecbe8)
@@ -35,4 +35,26 @@
 #include "devman.h"
 
+/** Compute compound score of driver and device.
+ *
+ * @param driver Match id of the driver.
+ * @param device Match id of the device.
+ * @return Compound score.
+ * @retval 0 No match at all.
+ */
+static int compute_match_score(match_id_t *driver, match_id_t *device)
+{
+	if (str_cmp(driver->id, device->id) == 0) {
+		/*
+		 * The strings match, return the product of their scores.
+		 */
+		return driver->score * device->score;
+	} else {
+		/*
+		 * Different strings, return zero.
+		 */
+		return 0;
+	}
+}
+
 int get_match_score(driver_t *drv, node_t *dev)
 {
@@ -43,63 +65,28 @@
 		return 0;
 	
+	/*
+	 * Go through all pairs, return the highest score obtained.
+	 */
+	int highest_score = 0;
+	
 	link_t *drv_link = drv->match_ids.ids.next;
-	link_t *dev_link = dev->match_ids.ids.next;
-	
-	match_id_t *drv_id = list_get_instance(drv_link, match_id_t, link);
-	match_id_t *dev_id = list_get_instance(dev_link, match_id_t, link);
-	
-	int score_next_drv = 0;
-	int score_next_dev = 0;
-	
-	do {
-		match_id_t *tmp_ma_id;
-	
-		if (str_cmp(drv_id->id, dev_id->id) == 0) {
-		 	/*
-		 	 * We found a match.
-		 	 * Return the score of the match.
-		 	 */
-			return drv_id->score * dev_id->score;
+	while (drv_link != drv_head) {
+		link_t *dev_link = dev_head->next;
+		while (dev_link != dev_head) {
+			match_id_t *drv_id = list_get_instance(drv_link, match_id_t, link);
+			match_id_t *dev_id = list_get_instance(dev_link, match_id_t, link);
+			
+			int score = compute_match_score(drv_id, dev_id);
+			if (score > highest_score) {
+				highest_score = score;
+			}
+
+			dev_link = dev_link->next;
 		}
 		
-		/*
-		 * Compute the next score we get, if we advance in the driver's
-		 * list of match ids.
-		 */
-		if (drv_link->next != drv_head) {
-			tmp_ma_id = list_get_instance(drv_link->next,
-			    match_id_t, link);
-			score_next_drv = dev_id->score * tmp_ma_id->score;
-		} else {
-			score_next_drv = 0;
-		}
-		
-		/*
-		 * Compute the next score we get, if we advance in the device's
-		 * list of match ids.
-		 */
-		if (dev_link->next != dev_head) {
-			tmp_ma_id = list_get_instance(dev_link->next,
-			    match_id_t, link);
-			score_next_dev = drv_id->score * tmp_ma_id->score;
-		} else {
-			score_next_dev = 0;
-		}
-		
-		/*
-		 * Advance in one of the two lists, so we get the next highest
-		 * score.
-		 */
-		if (score_next_drv > score_next_dev) {
-			drv_link = drv_link->next;
-			drv_id = list_get_instance(drv_link, match_id_t, link);
-		} else {
-			dev_link = dev_link->next;
-			dev_id = list_get_instance(dev_link, match_id_t, link);
-		}
-		
-	} while (drv_link->next != drv_head && dev_link->next != dev_head);
+		drv_link = drv_link->next;
+	}
 	
-	return 0;
+	return highest_score;
 }
 
