Index: uspace/lib/usb/src/hcdhubd.c
===================================================================
--- uspace/lib/usb/src/hcdhubd.c	(revision 8f62b0f40dc66ad23168df01feedb1b6f09e21af)
+++ uspace/lib/usb/src/hcdhubd.c	(revision 43178271ecca79c7c77563dbd2a252faa9b39f82)
@@ -31,31 +31,17 @@
  */
 /** @file
- * @brief HC driver and hub driver (implementation).
+ * @brief Common stuff for both HC driver and hub driver.
  */
 #include <usb/hcdhubd.h>
 #include <usb/devreq.h>
 #include <usbhc_iface.h>
+#include <usb/descriptor.h>
 #include <driver.h>
 #include <bool.h>
 #include <errno.h>
-
-#define USB_HUB_DEVICE_NAME "usbhub"
-
-/** List of handled host controllers. */
-static LIST_INITIALIZE(hc_list);
-
-/** Our HC driver. */
-static usb_hc_driver_t *hc_driver = NULL;
-
-static usbhc_iface_t usb_interface = {
-	.interrupt_out = NULL,
-	.interrupt_in = NULL
-};
-
-static device_ops_t usb_device_ops = {
-	.interfaces[USBHC_DEV_IFACE] = &usb_interface
-};
-
-static void set_hub_address(usb_hc_device_t *hc, usb_address_t address);
+#include <str_error.h>
+#include <usb/classes/hub.h>
+
+#include "hcdhubd_private.h"
 
 /** Callback when new device is detected and must be handled by this driver.
@@ -64,155 +50,6 @@
  * @return Error code.
  */
-static int add_device(device_t *dev)
-{
-	/*
-	 * FIXME: use some magic to determine whether hub or another HC
-	 * was connected.
-	 */
-	bool is_hc = str_cmp(dev->name, USB_HUB_DEVICE_NAME) != 0;
-	printf("%s: add_device(name=\"%s\")\n", hc_driver->name, dev->name);
-
-	if (is_hc) {
-		/*
-		 * We are the HC itself.
-		 */
-		usb_hc_device_t *hc_dev = malloc(sizeof(usb_hc_device_t));
-		list_initialize(&hc_dev->link);
-		hc_dev->transfer_ops = NULL;
-
-		hc_dev->generic = dev;
-		dev->ops = &usb_device_ops;
-		hc_dev->generic->driver_data = hc_dev;
-
-		int rc = hc_driver->add_hc(hc_dev);
-		if (rc != EOK) {
-			free(hc_dev);
-			return rc;
-		}
-
-		/*
-		 * FIXME: The following line causes devman to hang.
-		 * Will investigate later why.
-		 */
-		// add_device_to_class(dev, "usbhc");
-
-		list_append(&hc_dev->link, &hc_list);
-
-		return EOK;
-	} else {
-		usb_hc_device_t *hc = list_get_instance(hc_list.next, usb_hc_device_t, link);
-		set_hub_address(hc, 5);
-
-		/*
-		 * We are some (probably deeply nested) hub.
-		 * Thus, assign our own operations and explore already
-		 * connected devices.
-		 */
-
-		return ENOTSUP;
-	}
-}
-
-/** Sample usage of usb_hc_async functions.
- * This function sets hub address using standard SET_ADDRESS request.
- *
- * @warning This function shall be removed once you are familiar with
- * the usb_hc_ API.
- *
- * @param hc Host controller the hub belongs to.
- * @param address New hub address.
- */
-static void set_hub_address(usb_hc_device_t *hc, usb_address_t address)
-{
-	printf("%s: setting hub address to %d\n", hc->generic->name, address);
-	usb_target_t target = {0, 0};
-	usb_handle_t handle;
-	int rc;
-
-	usb_device_request_setup_packet_t setup_packet = {
-		.request_type = 0,
-		.request = USB_DEVREQ_SET_ADDRESS,
-		.index = 0,
-		.length = 0,
-	};
-	setup_packet.value = address;
-
-	rc = usb_hc_async_control_write_setup(hc, target,
-	    &setup_packet, sizeof(setup_packet), &handle);
-	if (rc != EOK) {
-		return;
-	}
-
-	rc = usb_hc_async_wait_for(handle);
-	if (rc != EOK) {
-		return;
-	}
-
-	rc = usb_hc_async_control_write_status(hc, target, &handle);
-	if (rc != EOK) {
-		return;
-	}
-
-	rc = usb_hc_async_wait_for(handle);
-	if (rc != EOK) {
-		return;
-	}
-
-	printf("%s: hub address changed\n", hc->generic->name);
-}
-
-/** Check changes on all known hubs.
- */
-static void check_hub_changes(void)
-{
-	/*
-	 * Iterate through all HCs.
-	 */
-	link_t *link_hc;
-	for (link_hc = hc_list.next;
-	    link_hc != &hc_list;
-	    link_hc = link_hc->next) {
-		usb_hc_device_t *hc = list_get_instance(link_hc,
-		    usb_hc_device_t, link);
-		/*
-		 * Iterate through all their hubs.
-		 */
-		link_t *link_hub;
-		for (link_hub = hc->hubs.next;
-		    link_hub != &hc->hubs;
-		    link_hub = link_hub->next) {
-			usb_hcd_hub_info_t *hub = list_get_instance(link_hub,
-			    usb_hcd_hub_info_t, link);
-
-			/*
-			 * Check status change pipe of this hub.
-			 */
-			usb_target_t target = {
-				.address = hub->device->address,
-				.endpoint = 1
-			};
-
-			// FIXME: count properly
-			size_t byte_length = (hub->port_count / 8) + 1;
-
-			void *change_bitmap = malloc(byte_length);
-			size_t actual_size;
-			usb_handle_t handle;
-
-			/*
-			 * Send the request.
-			 * FIXME: check returned value for possible errors
-			 */
-			usb_hc_async_interrupt_in(hc, target,
-			    change_bitmap, byte_length, &actual_size,
-			    &handle);
-
-			usb_hc_async_wait_for(handle);
-
-			/*
-			 * TODO: handle the changes.
-			 */
-		}
-	}
+static int add_device(device_t *dev) {
+	return ENOTSUP;
 }
 
@@ -234,15 +71,7 @@
  * @return Error code.
  */
-int usb_hcd_main(usb_hc_driver_t *hc)
-{
+int usb_hcd_main(usb_hc_driver_t *hc) {
 	hc_driver = hc;
 	hc_driver_generic.name = hc->name;
-
-	/*
-	 * Launch here fibril that will periodically check all
-	 * attached hubs for status change.
-	 * WARN: This call will effectively do nothing.
-	 */
-	check_hub_changes();
 
 	/*
@@ -261,20 +90,44 @@
  * @return Error code.
  */
-int usb_hcd_add_root_hub(usb_hc_device_t *dev)
-{
+int usb_hcd_add_root_hub(device_t *dev)
+{
+	char *id;
+	int rc = asprintf(&id, "usb&hub");
+	if (rc <= 0) {
+		return rc;
+	}
+
+	rc = usb_hc_add_child_device(dev, USB_HUB_DEVICE_NAME, id, true);
+	if (rc != EOK) {
+		free(id);
+	}
+
+	return rc;
+}
+
+/** Info about child device. */
+struct child_device_info {
+	device_t *parent;
+	const char *name;
+	const char *match_id;
+};
+
+/** Adds a child device fibril worker. */
+static int fibril_add_child_device(void *arg)
+{
+	struct child_device_info *child_info
+	    = (struct child_device_info *) arg;
 	int rc;
 
-	/*
-	 * Announce presence of child device.
-	 */
-	device_t *hub = NULL;
+	async_usleep(1000);
+
+	device_t *child = create_device();
 	match_id_t *match_id = NULL;
 
-	hub = create_device();
-	if (hub == NULL) {
+	if (child == NULL) {
 		rc = ENOMEM;
 		goto failure;
 	}
-	hub->name = USB_HUB_DEVICE_NAME;
+	child->name = child_info->name;
 
 	match_id = create_match_id();
@@ -283,33 +136,90 @@
 		goto failure;
 	}
-
-	char *id;
-	rc = asprintf(&id, "usb&hc=%s&hub", dev->generic->name);
-	if (rc <= 0) {
-		rc = ENOMEM;
-		goto failure;
-	}
-
-	match_id->id = id;
-	match_id->score = 30;
-
-	add_match_id(&hub->match_ids, match_id);
-
-	rc = child_device_register(hub, dev->generic);
+	match_id->id = child_info->match_id;
+	match_id->score = 10;
+	add_match_id(&child->match_ids, match_id);
+
+	printf("%s: adding child device `%s' with match \"%s\"\n",
+	    hc_driver->name, child->name, match_id->id);
+	rc = child_device_register(child, child_info->parent);
+	printf("%s: child device `%s' registration: %s\n",
+	    hc_driver->name, child->name, str_error(rc));
+
 	if (rc != EOK) {
 		goto failure;
 	}
 
-	printf("%s: registered root hub\n", dev->generic->name);
+	goto leave;
+
+failure:
+	if (child != NULL) {
+		child->name = NULL;
+		delete_device(child);
+	}
+
+	if (match_id != NULL) {
+		match_id->id = NULL;
+		delete_match_id(match_id);
+	}
+
+leave:
+	free(arg);
 	return EOK;
-
-failure:
-	if (hub != NULL) {
-		hub->name = NULL;
-		delete_device(hub);
-	}
-	delete_match_id(match_id);
-
-	return rc;
+}
+
+/** Adds a child.
+ * Due to deadlock in devman when parent registers child that oughts to be
+ * driven by the same task, the child adding is done in separate fibril.
+ * Not optimal, but it works.
+ * Update: not under all circumstances the new fibril is successful either.
+ * Thus the last parameter to let the caller choose.
+ *
+ * @param parent Parent device.
+ * @param name Device name.
+ * @param match_id Match id.
+ * @param create_fibril Whether to run the addition in new fibril.
+ * @return Error code.
+ */
+int usb_hc_add_child_device(device_t *parent, const char *name,
+    const char *match_id, bool create_fibril)
+{
+	printf("%s: about to add child device `%s' (%s)\n", hc_driver->name,
+	    name, match_id);
+
+	/*
+	 * Seems that creating fibril which postpones the action
+	 * is the best solution.
+	 */
+	create_fibril = true;
+
+	struct child_device_info *child_info
+	    = malloc(sizeof(struct child_device_info));
+
+	child_info->parent = parent;
+	child_info->name = name;
+	child_info->match_id = match_id;
+
+	if (create_fibril) {
+		fid_t fibril = fibril_create(fibril_add_child_device, child_info);
+		if (!fibril) {
+			return ENOMEM;
+		}
+		fibril_add_ready(fibril);
+	} else {
+		fibril_add_child_device(child_info);
+	}
+
+	return EOK;
+}
+
+/** Tell USB address of given device.
+ *
+ * @param handle Devman handle of the device.
+ * @return USB device address or error code.
+ */
+usb_address_t usb_get_address_by_handle(devman_handle_t handle)
+{
+	/* TODO: search list of attached devices. */
+	return ENOENT;
 }
 
