Index: uspace/drv/bus/usb/usbhub/port.c
===================================================================
--- uspace/drv/bus/usb/usbhub/port.c	(revision 51a51be943417c8bcd0347b01f2a7a91627a6850)
+++ uspace/drv/bus/usb/usbhub/port.c	(revision a9fcd73c9a825936be7838244fd03da4f23bf1e7)
@@ -58,37 +58,13 @@
 	assert(port);
 	memset(port, 0, sizeof(*port));
-	fibril_mutex_initialize(&port->guard);
-	fibril_condvar_initialize(&port->state_cv);
 	port->hub = hub;
 	port->port_number = port_number;
-}
-
-/**
- * Utility method to change current port state and notify waiters.
- */
-static void port_change_state(usb_hub_port_t *port, port_state_t state)
-{
-	assert(fibril_mutex_is_locked(&port->guard));
-#define S(STATE) [PORT_##STATE] = #STATE
-	static const char *state_names [] = {
-	    S(DISABLED), S(CONNECTED), S(ERROR), S(IN_RESET), S(ENABLED),
-	};
-#undef S
-	port_log(debug, port, "%s ->%s", state_names[port->state], state_names[state]);
-	port->state = state;
-	fibril_condvar_broadcast(&port->state_cv);
-}
-
-/**
- * Utility method to wait for a particular state.
- *
- * @warning Some states might not be reached because of an external error
- * condition (PORT_ENABLED).
- */
-static void port_wait_state(usb_hub_port_t *port, port_state_t state)
-{
-	assert(fibril_mutex_is_locked(&port->guard));
-	while (port->state != state)
-		fibril_condvar_wait(&port->state_cv, &port->guard);
+	usb_port_init(&port->base);
+}
+
+static inline usb_hub_port_t *get_hub_port(usb_port_t *port)
+{
+	assert(port);
+	return (usb_hub_port_t *) port;
 }
 
@@ -96,223 +72,80 @@
  * Inform the HC that the device on port is gone.
  */
-static int usb_hub_port_device_gone(usb_hub_port_t *port)
-{
-	assert(port);
-	assert(fibril_mutex_is_locked(&port->guard));
-	assert(port->state == PORT_ENABLED);
+static void remove_device(usb_port_t *port_base)
+{
+	usb_hub_port_t *port = get_hub_port(port_base);
 
 	async_exch_t *exch = usb_device_bus_exchange_begin(port->hub->usb_device);
-	if (!exch)
+	if (!exch) {
+		port_log(error, port, "Cannot remove the device, failed creating exchange.");
+		return;
+	}
+	
+	const int err = usbhc_device_remove(exch, port->port_number);
+	if (err)
+		port_log(error, port, "Failed to remove device: %s", str_error(err));
+
+	usb_device_bus_exchange_end(exch);
+}
+
+/**
+ * Routine for adding a new device.
+ *
+ * Separate fibril is needed because the operation blocks on waiting for
+ * requesting default address and resetting port, and we must not block the
+ * control pipe.
+ */
+static int enumerate_device(usb_port_t *port_base)
+{
+	int err;
+	usb_hub_port_t *port = get_hub_port(port_base);
+
+	port_log(debug, port, "Setting up new device.");
+
+	async_exch_t *exch = usb_device_bus_exchange_begin(port->hub->usb_device);
+	if (!exch) {
+		port_log(error, port, "Failed to create exchange.");
 		return ENOMEM;
-	const int rc = usbhc_device_remove(exch, port->port_number);
-	usb_device_bus_exchange_end(exch);
-	return rc;
-}
-
-/**
- * Teardown a device on port, no matter which state it was in.
- */
-static void port_make_disabled(usb_hub_port_t *port)
-{
-	assert(fibril_mutex_is_locked(&port->guard));
-
-	port_log(debug, port, "Making device offline.");
-
-	switch (port->state) {
-	case PORT_ENABLED:
-		port_log(debug, port, "Enabled ->");
-		if (usb_hub_port_device_gone(port))
-			port_log(error, port, "Failed to remove the device node from HC. Continuing anyway.");
-		port_change_state(port, PORT_DISABLED);
-		break;
-
-	case PORT_CONNECTED:
-		port_log(debug, port, "Connected ->");
-		/* fallthrough */
-	case PORT_IN_RESET:
-		port_log(debug, port, "In reset ->");
-		port_change_state(port, PORT_ERROR);
-		/* fallthrough */
-	case PORT_ERROR:
-		port_log(debug, port, "Error ->");
-		port_wait_state(port, PORT_DISABLED);
-		/* fallthrough */
-	case PORT_DISABLED:
-		port_log(debug, port, "Disabled.");
-		break;
-	}
-
-	assert(port->state == PORT_DISABLED);
-}
-
-/**
- * Finalize a port. Make sure no fibril is managing its state,
- * and that the HC is aware the device is no longer there.
- */
-void usb_hub_port_fini(usb_hub_port_t *port)
-{
-	assert(port);
-
-	fibril_mutex_lock(&port->guard);
-	switch (port->state) {
-	case PORT_ENABLED:
-		/*
-		 * We shall inform the HC that the device is gone.
-		 * However, we can't wait for it, because if the device is hub,
-		 * it would have to use the same IPC handling fibril as we do.
-		 * But we cannot even defer it to another fibril, because then
-		 * the HC would assume our driver didn't cleanup properly, and
-		 * will remove those devices by himself.
-		 *
-		 * So the solutions seems to behave like a bad driver and leave
-		 * the work for HC.
-		 */
-		port_change_state(port, PORT_DISABLED);
-		break;
-
-	case PORT_CONNECTED:
-	case PORT_IN_RESET:
-		port_change_state(port, PORT_ERROR);
-		/* fallthrough */
-	case PORT_ERROR:
-		port_wait_state(port, PORT_DISABLED);
-		/* fallthrough */
-	case PORT_DISABLED:
-		break;
-	}
-	port_log(debug, port, "Finalized.");
-	fibril_mutex_unlock(&port->guard);
-}
-
-static int port_reset_sync(usb_hub_port_t *port)
-{
-	assert(fibril_mutex_is_locked(&port->guard));
-	assert(port->state == PORT_CONNECTED);
-
-	port_change_state(port, PORT_IN_RESET);
-	port_log(debug2, port, "Issuing reset.");
+	}
+
+	/* Reserve default address */
+	err = usb_hub_reserve_default_address(port->hub, exch, &port->base);
+	if (err != EOK) {
+		port_log(error, port, "Failed to reserve default address: %s", str_error(err));
+		goto out_exch;
+	}
+
+	/* Reservation of default address could have blocked */
+	if (port->base.state != PORT_CONNECTING)
+		goto out_address;
+
+	port_log(debug, port, "Got default address. Resetting port.");
 	int rc = usb_hub_set_port_feature(port->hub, port->port_number, USB_HUB_FEATURE_PORT_RESET);
 	if (rc != EOK) {
 		port_log(warning, port, "Port reset request failed: %s", str_error(rc));
-		return rc;
-	}
-
-	fibril_condvar_wait_timeout(&port->state_cv, &port->guard, 2000000);
-	return port->state == PORT_ENABLED ? EOK : ESTALL;
-}
-
-/**
- * Routine for adding a new device.
- *
- * Separate fibril is needed because the operation blocks on waiting for
- * requesting default address and resetting port, and we must not block the
- * control pipe.
- */
-static void setup_device(usb_hub_port_t *port)
-{
-	int err;
-
-	fibril_mutex_lock(&port->guard);
-
-	if (port->state == PORT_ERROR) {
-		/* 
-		 * The device was removed faster than this fibril acquired the
-		 * mutex.
-		 */
-		port_change_state(port, PORT_DISABLED);
-		goto out;
-	}
-
-	if (port->state != PORT_CONNECTED) {
-		/*
-		 * Another fibril already took care of the device.
-		 * This may happen for example when the connection is unstable
-		 * and a sequence of connect, disconnect and connect come
-		 * faster the first fibril manages to request the default
-		 * address.
-		 */
-		goto out;
-	}
-
-	port_log(debug, port, "Setting up new device.");
-
-	async_exch_t *exch = usb_device_bus_exchange_begin(port->hub->usb_device);
-	if (!exch) {
-		port_log(error, port, "Failed to create exchange.");
-		goto out;
-	}
-
-	/* Reserve default address */
-	err = usb_hub_reserve_default_address(port->hub, exch, &port->guard);
-	if (err != EOK) {
-		port_log(error, port, "Failed to reserve default address: %s", str_error(err));
-		port_change_state(port, PORT_DISABLED);
-		goto out_exch;
-	}
-
-	/* Reservation of default address could have blocked */
-	if (port->state != PORT_CONNECTED) {
-		assert(port->state == PORT_ERROR);
-		port_change_state(port, PORT_DISABLED);
-		goto out_exch;
-	}
-
-	port_log(debug, port, "Got default address. Resetting port.");
-
-	if ((err = port_reset_sync(port))) {
-		port_log(error, port, "Failed to reset port.");
-		port_change_state(port, PORT_DISABLED);
-		goto out_address;
-	}
-
-	assert(port->state == PORT_ENABLED);
+		goto out_address;
+	}
+
+	if ((err = usb_port_wait_for_enabled(&port->base))) {
+		port_log(error, port, "Failed to reset port: %s", str_error(err));
+		goto out_address;
+	}
 
 	port_log(debug, port, "Port reset, enumerating device.");
 
-	if ((err = usbhc_device_enumerate(exch, port->port_number, port->speed))) {
+	if ((err = usbhc_device_enumerate(exch, port->port_number, port->base.speed))) {
 		port_log(error, port, "Failed to enumerate device: %s", str_error(err));
-		port_change_state(port, PORT_DISABLED);
-		goto out_port;
+		/* Disable the port */
+		usb_hub_clear_port_feature(port->hub, port->port_number, USB_HUB_FEATURE_PORT_ENABLE);
+		goto out_address;
 	}
 
 	port_log(debug, port, "Device enumerated");
 
-out_port:
-	if (port->state != PORT_ENABLED)
-		usb_hub_clear_port_feature(port->hub, port->port_number, USB_HUB_FEATURE_C_PORT_ENABLE);
 out_address:
 	usb_hub_release_default_address(port->hub, exch);
 out_exch:
 	usb_device_bus_exchange_end(exch);
-out:
-	assert(port->state == PORT_ENABLED || port->state == PORT_DISABLED);
-	fibril_mutex_unlock(&port->guard);
-}
-
-static int setup_device_worker(void *arg)
-{
-	setup_device(arg);
-	return EOK;
-}
-
-/** Start device adding when connection change is detected.
- *
- * This fires a new fibril to complete the device addition.
- *
- * @param hub Hub where the change occured.
- * @param port Port index (starting at 1).
- * @param speed Speed of the device.
- * @return Error code.
- */
-static int create_setup_device_fibril(usb_hub_port_t *port)
-{
-	assert(port);
-
-	fid_t fibril = fibril_create(setup_device_worker, port);
-	if (!fibril)
-		return ENOMEM;
-
-	fibril_add_ready(fibril);
-	return EOK;
+	return err;
 }
 
@@ -323,13 +156,7 @@
 
 	if (connected) {
-		if (port->state == PORT_ENABLED)
-			port_log(warning, port, "Connection detected on port that is currently enabled. Resetting.");
-
-		port_make_disabled(port);
-		port_change_state(port, PORT_CONNECTED);
-		port->speed = usb_port_speed(status);
-		create_setup_device_fibril(port);
+		usb_port_connected(&port->base, &enumerate_device);
 	} else {
-		port_make_disabled(port);
+		usb_port_disabled(&port->base, &remove_device);
 	}
 }
@@ -341,5 +168,5 @@
 		port_log(warning, port, "Port unexpectedly changed to enabled.");
 	} else {
-		port_make_disabled(port);
+		usb_port_disabled(&port->base, &remove_device);
 	}
 }
@@ -360,5 +187,5 @@
 	 * back on when the over-current condition is gone */
 
-	port_make_disabled(port);
+	usb_port_disabled(&port->base, &remove_device);
 
 	if (!overcurrent) {
@@ -373,9 +200,8 @@
 	const bool enabled = !!(status & USB_HUB_PORT_STATUS_ENABLED);
 
-	/* Check if someone is waiting for the result */
-	if (port->state != PORT_IN_RESET)
-		return;
-
-	port_change_state(port, enabled ? PORT_ENABLED : PORT_ERROR);
+	if (enabled)
+		usb_port_enabled(&port->base, usb_port_speed(status));
+	else
+		usb_port_disabled(&port->base, &remove_device);
 }
 
@@ -398,8 +224,7 @@
  * @param hub hub representation
  */
-void usb_hub_port_process_interrupt(usb_hub_port_t *port, usb_hub_dev_t *hub)
+void usb_hub_port_process_interrupt(usb_hub_port_t *port)
 {
 	assert(port);
-	assert(hub);
 	port_log(debug2, port, "Interrupt.");
 
@@ -411,6 +236,4 @@
 	}
 
-	fibril_mutex_lock(&port->guard);
-
 	for (uint32_t feature = 0; feature < sizeof(usb_port_status_t) * 8; ++feature) {
 		uint32_t mask = 1 << feature;
@@ -429,6 +252,4 @@
 	}
 
-	fibril_mutex_unlock(&port->guard);
-
 	port_log(debug2, port, "Port status after handling: %#08" PRIx32, status);
 }
