Index: uspace/srv/hw/bus/usb/hcd/virtual/hub.c
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/hub.c	(revision b8507a17eb6fca4a7b5ebb922a908862b9c64b5e)
+++ uspace/srv/hw/bus/usb/hcd/virtual/hub.c	(revision 6c741e1df12ae6b7434212a5281ceaf8e0a87d19)
@@ -140,5 +140,5 @@
 	.descriptors = &descriptors,
 };
-
+ 
 hub_device_t hub_dev;
 
@@ -147,8 +147,10 @@
 	size_t i;
 	for (i = 0; i < HUB_PORT_COUNT; i++) {
-		hub_dev.ports[i].device = NULL;
-		hub_dev.ports[i].state = HUB_PORT_STATE_NOT_CONFIGURED;
-	}
-	hub_dev.status_change_bitmap = 0;
+		hub_port_t *port = &hub_dev.ports[i];
+		
+		port->device = NULL;
+		port->state = HUB_PORT_STATE_NOT_CONFIGURED;
+		port->status_change = 0;
+	}
 	
 	usbvirt_connect_local(&virthub_dev);
@@ -161,12 +163,24 @@
 	size_t i;
 	for (i = 0; i < HUB_PORT_COUNT; i++) {
-		if (hub_dev.ports[i].device != NULL) {
+		hub_port_t *port = &hub_dev.ports[i];
+		
+		if (port->device != NULL) {
 			continue;
 		}
-		hub_dev.ports[i].device = device;
-		// TODO - notify the host about change
-		// bad, bad but it will work somehow at least
-		hub_dev.ports[i].state = HUB_PORT_STATE_ENABLED;
-		hub_dev.status_change_bitmap |= (1 << (i+1));
+		
+		port->device = device;
+		
+		/*
+		 * TODO:
+		 * If the hub was configured, we can normally
+		 * announce the plug-in.
+		 * Otherwise, we will wait until hub is configured
+		 * and announce changes in single burst.
+		 */
+		//if (port->state == HUB_PORT_STATE_DISCONNECTED) {
+			port->state = HUB_PORT_STATE_DISABLED;
+			set_port_status_change(port, HUB_STATUS_C_PORT_CONNECTION);
+		//}
+		
 		return i;
 	}
@@ -180,11 +194,14 @@
 	size_t i;
 	for (i = 0; i < HUB_PORT_COUNT; i++) {
-		if (hub_dev.ports[i].device != device) {
+		hub_port_t *port = &hub_dev.ports[i];
+		
+		if (port->device != device) {
 			continue;
 		}
-		hub_dev.ports[i].device = NULL;
-		hub_dev.ports[i].state = HUB_PORT_STATE_DISCONNECTED;
-		hub_dev.status_change_bitmap |= (1 << (i+1));
-		// TODO - notify the host of the removal
+		
+		port->device = NULL;
+		port->state = HUB_PORT_STATE_DISCONNECTED;
+		
+		set_port_status_change(port, HUB_STATUS_C_PORT_CONNECTION);
 	}
 }
@@ -202,4 +219,24 @@
 }
 
+void hub_check_port_changes(void)
+{
+	/* FIXME - what if HUB_PORT_COUNT is greater than 8. */
+	uint8_t change_map = 0;
+	
+	size_t i;
+	for (i = 0; i < HUB_PORT_COUNT; i++) {
+		hub_port_t *port = &hub_dev.ports[i];
+		
+		if (port->status_change != 0) {
+			change_map |= (1 << (i + 1));
+		}
+	}
+	
+	/* FIXME - do not send when it has not changed since previous run. */
+	if (change_map != 0) {
+		virthub_dev.send_data(&virthub_dev, HUB_STATUS_CHANGE_PIPE,
+		    &change_map, 1);
+	}
+}
 
 /**
Index: uspace/srv/hw/bus/usb/hcd/virtual/hub.h
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/hub.h	(revision b8507a17eb6fca4a7b5ebb922a908862b9c64b5e)
+++ uspace/srv/hw/bus/usb/hcd/virtual/hub.h	(revision 6c741e1df12ae6b7434212a5281ceaf8e0a87d19)
@@ -51,4 +51,5 @@
 void hub_remove_device(virtdev_connection_t *);
 bool hub_can_device_signal(virtdev_connection_t *);
+void hub_check_port_changes(void);
 
 #endif
Index: uspace/srv/hw/bus/usb/hcd/virtual/hubintern.h
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/hubintern.h	(revision b8507a17eb6fca4a7b5ebb922a908862b9c64b5e)
+++ uspace/srv/hw/bus/usb/hcd/virtual/hubintern.h	(revision 6c741e1df12ae6b7434212a5281ceaf8e0a87d19)
@@ -78,13 +78,21 @@
 } hub_port_state_t;
 
+typedef enum {
+	HUB_STATUS_C_PORT_CONNECTION = (1 << 0),
+	HUB_STATUS_C_PORT_ENABLE = (1 << 1),
+	HUB_STATUS_C_PORT_SUSPEND = (1 << 2),
+	HUB_STATUS_C_PORT_OVER_CURRENT = (1 << 3),
+	HUB_STATUS_C_PORT_RESET = (1 << 4),
+	/* HUB_STATUS_C_ = (1 << ), */
+} hub_status_change_t;
+
 typedef struct {
 	virtdev_connection_t *device;
 	hub_port_state_t state;
+	uint16_t status_change;
 } hub_port_t;
 
 typedef struct {
 	hub_port_t ports[HUB_PORT_COUNT];
-	/* FIXME - assuming HUB_PORT_COUNT < 8 */
-	uint8_t status_change_bitmap;
 } hub_device_t;
 
@@ -95,4 +103,8 @@
 extern usbvirt_device_ops_t hub_ops;
 
+void clear_port_status_change(hub_port_t *, uint16_t);
+void set_port_status_change(hub_port_t *, uint16_t);
+
+
 #endif
 /**
Index: uspace/srv/hw/bus/usb/hcd/virtual/hubops.c
===================================================================
--- uspace/srv/hw/bus/usb/hcd/virtual/hubops.c	(revision b8507a17eb6fca4a7b5ebb922a908862b9c64b5e)
+++ uspace/srv/hw/bus/usb/hcd/virtual/hubops.c	(revision 6c741e1df12ae6b7434212a5281ceaf8e0a87d19)
@@ -43,4 +43,16 @@
 #include "hubintern.h"
 
+#define MAKE_BYTE(b0, b1, b2, b3, b4, b5, b6, b7) \
+	(( \
+		((b0) << 0) \
+		| ((b1) << 1) \
+		| ((b2) << 2) \
+		| ((b3) << 3) \
+		| ((b4) << 4) \
+		| ((b5) << 5) \
+		| ((b6) << 6) \
+		| ((b7) << 7) \
+	))
+
 static int on_get_descriptor(struct usbvirt_device *dev,
     usb_device_request_setup_packet_t *request, uint8_t *data);
@@ -82,4 +94,41 @@
 }
 
+/** Change port status and updates status change status fields.
+ */
+static void set_port_state(hub_port_t *port, hub_port_state_t state)
+{
+	port->state = state;
+	if (state == HUB_PORT_STATE_POWERED_OFF) {
+		clear_port_status_change(port, HUB_STATUS_C_PORT_CONNECTION);
+		clear_port_status_change(port, HUB_STATUS_C_PORT_ENABLE);
+		clear_port_status_change(port, HUB_STATUS_C_PORT_RESET);
+	}
+	if (state == HUB_PORT_STATE_RESUMING) {
+		async_usleep(10*1000);
+		if (port->state == state) {
+			set_port_state(port, HUB_PORT_STATE_ENABLED);
+		}
+	}
+	if (state == HUB_PORT_STATE_RESETTING) {
+		async_usleep(10*1000);
+		if (port->state == state) {
+			set_port_status_change(port, HUB_STATUS_C_PORT_RESET);
+			set_port_state(port, HUB_PORT_STATE_ENABLED);
+		}
+	}
+}
+
+#define _GET_PORT(portvar, index) \
+	do { \
+		if (virthub_dev.state != USBVIRT_STATE_CONFIGURED) { \
+			return EINVAL; \
+		} \
+		if (((index) == 0) || ((index) > HUB_PORT_COUNT)) { \
+			return EINVAL; \
+		} \
+	} while (false); \
+	hub_port_t *portvar = &hub_dev.ports[index]
+
+
 static int clear_hub_feature(uint16_t feature)
 {
@@ -88,5 +137,45 @@
 
 static int clear_port_feature(uint16_t feature, uint16_t portindex)
-{
+{	
+	_GET_PORT(port, portindex);
+	
+	switch (feature) {
+		case USB_HUB_FEATURE_PORT_ENABLE:
+			if ((port->state != HUB_PORT_STATE_NOT_CONFIGURED)
+			    && (port->state != HUB_PORT_STATE_POWERED_OFF)) {
+				set_port_state(port, HUB_PORT_STATE_DISABLED);
+			}
+			return EOK;
+		
+		case USB_HUB_FEATURE_PORT_SUSPEND:
+			if (port->state != HUB_PORT_STATE_SUSPENDED) {
+				return EOK;
+			}
+			set_port_state(port, HUB_PORT_STATE_RESUMING);
+			return EOK;
+			
+		case USB_HUB_FEATURE_PORT_POWER:
+			if (port->state != HUB_PORT_STATE_NOT_CONFIGURED) {
+				set_port_state(port, HUB_PORT_STATE_POWERED_OFF);
+			}
+			return EOK;
+		
+		case USB_HUB_FEATURE_C_PORT_CONNECTION:
+			clear_port_status_change(port, HUB_STATUS_C_PORT_CONNECTION);
+			return EOK;
+		
+		case USB_HUB_FEATURE_C_PORT_ENABLE:
+			clear_port_status_change(port, HUB_STATUS_C_PORT_ENABLE);
+			return EOK;
+		
+		case USB_HUB_FEATURE_C_PORT_SUSPEND:
+			clear_port_status_change(port, HUB_STATUS_C_PORT_SUSPEND);
+			return EOK;
+			
+		case USB_HUB_FEATURE_C_PORT_OVER_CURRENT:
+			clear_port_status_change(port, HUB_STATUS_C_PORT_OVER_CURRENT);
+			return EOK;
+	}
+	
 	return ENOTSUP;
 }
@@ -105,10 +194,41 @@
 static int get_hub_status(void)
 {
-	return ENOTSUP;
+	uint32_t hub_status = 0;
+	
+	return virthub_dev.send_data(&virthub_dev, 0, &hub_status, 4);
 }
 
 static int get_port_status(uint16_t portindex)
 {
-	return ENOTSUP;
+	_GET_PORT(port, portindex);
+	
+	uint32_t status;
+	status = MAKE_BYTE(
+	    /* Current connect status. */
+	    port->device == NULL ? 0 : 1,
+	    /* Port enabled/disabled. */
+	    port->state == HUB_PORT_STATE_ENABLED ? 1 : 0,
+	    /* Suspend. */
+	    (port->state == HUB_PORT_STATE_SUSPENDED)
+	        || (port->state == HUB_PORT_STATE_RESUMING) ? 1 : 0,
+	    /* Over-current. */
+	    0,
+	    /* Reset. */
+	    port->state == HUB_PORT_STATE_RESETTING ? 1 : 0,
+	    /* Reserved. */
+	    0, 0, 0)
+	    
+	    | (MAKE_BYTE(
+	    /* Port power. */
+	    port->state == HUB_PORT_STATE_POWERED_OFF ? 0 : 1,
+	    /* Full-speed device. */
+	    0,
+	    /* Reserved. */
+	    0, 0, 0, 0, 0, 0
+	    )) << 8;
+	    
+	status |= (port->status_change << 16);
+	
+	return virthub_dev.send_data(&virthub_dev, 0, &status, 4);
 }
 
@@ -121,6 +241,31 @@
 static int set_port_feature(uint16_t feature, uint16_t portindex)
 {
-	return ENOTSUP;
-}
+	_GET_PORT(port, portindex);
+	
+	switch (feature) {
+		case USB_HUB_FEATURE_PORT_RESET:
+			if (port->state != HUB_PORT_STATE_POWERED_OFF) {
+				set_port_state(port, HUB_PORT_STATE_RESETTING);
+			}
+			return EOK;
+		
+		case USB_HUB_FEATURE_PORT_SUSPEND:
+			if (port->state == HUB_PORT_STATE_ENABLED) {
+				set_port_state(port, HUB_PORT_STATE_SUSPENDED);
+			}
+			return EOK;
+		
+		case USB_HUB_FEATURE_PORT_POWER:
+			if (port->state == HUB_PORT_STATE_POWERED_OFF) {
+				set_port_state(port, HUB_PORT_STATE_DISCONNECTED);
+			}
+			return EOK;
+	}
+	return ENOTSUP;
+}
+
+#undef _GET_PORT
+
+
 
 
@@ -186,4 +331,15 @@
 }
 
+void clear_port_status_change(hub_port_t *port, uint16_t change)
+{
+	port->status_change &= (~change);
+	hub_check_port_changes();
+}
+
+void set_port_status_change(hub_port_t *port, uint16_t change)
+{
+	port->status_change |= change;
+}
+
 /**
  * @}
