Index: uspace/lib/usb/src/devdrv.c
===================================================================
--- uspace/lib/usb/src/devdrv.c	(revision 15a9e63987fa5c5f92fa640a7f17336dab5c09c1)
+++ uspace/lib/usb/src/devdrv.c	(revision f8e8738d31d2b92e00a460fe812bda69bfb6ef51)
@@ -36,4 +36,5 @@
 #include <usb/request.h>
 #include <usb/debug.h>
+#include <usb/dp.h>
 #include <errno.h>
 #include <str_error.h>
@@ -86,12 +87,12 @@
  * @return Number of pipes (excluding default control pipe).
  */
-static size_t count_other_pipes(usb_driver_t *drv)
+static size_t count_other_pipes(usb_endpoint_description_t **endpoints)
 {
 	size_t count = 0;
-	if (drv->endpoints == NULL) {
+	if (endpoints == NULL) {
 		return 0;
 	}
 
-	while (drv->endpoints[count] != NULL) {
+	while (endpoints[count] != NULL) {
 		count++;
 	}
@@ -106,10 +107,14 @@
  * @return Error code.
  */
-static int initialize_other_pipes(usb_driver_t *drv, usb_device_t *dev)
+static int initialize_other_pipes(usb_endpoint_description_t **endpoints,
+    usb_device_t *dev)
 {
 	int rc;
-	dev->interface_no = usb_device_get_assigned_interface(dev->ddf_dev);
-
-	size_t pipe_count = count_other_pipes(drv);
+
+	size_t pipe_count = count_other_pipes(endpoints);
+	if (pipe_count == 0) {
+		return EOK;
+	}
+
 	dev->pipes = malloc(sizeof(usb_endpoint_mapping_t) * pipe_count);
 	if (dev->pipes == NULL) {
@@ -133,6 +138,7 @@
 		}
 
-		dev->pipes[i].description = drv->endpoints[i];
+		dev->pipes[i].description = endpoints[i];
 		dev->pipes[i].interface_no = dev->interface_no;
+		dev->pipes[i].interface_setting = 0;
 	}
 
@@ -178,4 +184,6 @@
 	usb_hc_connection_close(&hc_conn);
 
+	dev->pipes_count = pipe_count;
+
 	return EOK;
 
@@ -227,4 +235,7 @@
 	}
 
+	/* Get our interface. */
+	dev->interface_no = usb_device_get_assigned_interface(dev->ddf_dev);
+
 	/*
 	 * For further actions, we need open session on default control pipe.
@@ -251,5 +262,5 @@
 	    &dev->descriptors.configuration_size);
 	if (rc != EOK) {
-		usb_log_error("Failed retrieving configuration descriptor: %s.\n",
+		usb_log_error("Failed retrieving configuration descriptor: %s. %s\n",
 		    dev->ddf_dev->name, str_error(rc));
 		return rc;
@@ -257,5 +268,5 @@
 
 	if (driver->endpoints != NULL) {
-		rc = initialize_other_pipes(driver, dev);
+		rc = initialize_other_pipes(driver->endpoints, dev);
 	}
 
@@ -271,4 +282,128 @@
 
 	return rc;
+}
+
+/** Count number of alternate settings of a interface.
+ *
+ * @param config_descr Full configuration descriptor.
+ * @param config_descr_size Size of @p config_descr in bytes.
+ * @param interface_no Interface number.
+ * @return Number of alternate interfaces for @p interface_no interface.
+ */
+static size_t count_alternate_interfaces(uint8_t *config_descr,
+    size_t config_descr_size, int interface_no)
+{
+	assert(config_descr != NULL);
+	usb_dp_parser_t dp_parser = {
+		.nesting = usb_dp_standard_descriptor_nesting
+	};
+	usb_dp_parser_data_t dp_data = {
+		.data = config_descr,
+		.size = config_descr_size,
+		.arg = NULL
+	};
+
+	size_t alternate_count = 0;
+
+	uint8_t *iface_ptr = usb_dp_get_nested_descriptor(&dp_parser,
+	    &dp_data, config_descr);
+	while (iface_ptr != NULL) {
+		usb_standard_interface_descriptor_t *iface
+		    = (usb_standard_interface_descriptor_t *) iface_ptr;
+		if (iface->descriptor_type == USB_DESCTYPE_INTERFACE) {
+			if (iface->interface_number == interface_no) {
+				alternate_count++;
+			}
+		}
+		iface_ptr = usb_dp_get_sibling_descriptor(&dp_parser, &dp_data,
+		    config_descr, iface_ptr);
+	}
+
+	return alternate_count;
+}
+
+/** Initialize structures related to alternate interfaces.
+ *
+ * @param dev Device where alternate settings shall be initialized.
+ * @return Error code.
+ */
+static int initialize_alternate_interfaces(usb_device_t *dev)
+{
+	if (dev->interface_no < 0) {
+		dev->alternate_interfaces = NULL;
+		return EOK;
+	}
+
+	usb_alternate_interfaces_t *alternates
+	    = malloc(sizeof(usb_alternate_interfaces_t));
+
+	if (alternates == NULL) {
+		return ENOMEM;
+	}
+
+	alternates->alternative_count
+	    = count_alternate_interfaces(dev->descriptors.configuration,
+	    dev->descriptors.configuration_size, dev->interface_no);
+
+	if (alternates->alternative_count == 0) {
+		free(alternates);
+		return ENOENT;
+	}
+
+	alternates->alternatives = malloc(alternates->alternative_count
+	    * sizeof(usb_alternate_interface_descriptors_t));
+	if (alternates->alternatives == NULL) {
+		free(alternates);
+		return ENOMEM;
+	}
+
+	alternates->current = 0;
+
+	usb_dp_parser_t dp_parser = {
+		.nesting = usb_dp_standard_descriptor_nesting
+	};
+	usb_dp_parser_data_t dp_data = {
+		.data = dev->descriptors.configuration,
+		.size = dev->descriptors.configuration_size,
+		.arg = NULL
+	};
+
+	usb_alternate_interface_descriptors_t *cur_alt_iface
+	    = &alternates->alternatives[0];
+
+	uint8_t *iface_ptr = usb_dp_get_nested_descriptor(&dp_parser,
+	    &dp_data, dp_data.data);
+	while (iface_ptr != NULL) {
+		usb_standard_interface_descriptor_t *iface
+		    = (usb_standard_interface_descriptor_t *) iface_ptr;
+		if ((iface->descriptor_type != USB_DESCTYPE_INTERFACE)
+		    || (iface->interface_number != dev->interface_no)) {
+			iface_ptr = usb_dp_get_sibling_descriptor(&dp_parser,
+			    &dp_data,
+			    dp_data.data, iface_ptr);
+			continue;
+		}
+
+		cur_alt_iface->interface = iface;
+		cur_alt_iface->nested_descriptors = iface_ptr + sizeof(*iface);
+
+		/* Find next interface to count size of nested descriptors. */
+		iface_ptr = usb_dp_get_sibling_descriptor(&dp_parser, &dp_data,
+		    dp_data.data, iface_ptr);
+		if (iface_ptr == NULL) {
+			uint8_t *next = dp_data.data + dp_data.size;
+			cur_alt_iface->nested_descriptors_size
+			    = next - cur_alt_iface->nested_descriptors;
+		} else {
+			cur_alt_iface->nested_descriptors_size
+			    = iface_ptr - cur_alt_iface->nested_descriptors;
+		}
+
+		cur_alt_iface++;
+	}
+
+	dev->alternate_interfaces = alternates;
+
+	return EOK;
 }
 
@@ -301,4 +436,7 @@
 	dev->descriptors.configuration = NULL;
 
+	dev->pipes_count = 0;
+	dev->pipes = NULL;
+
 	rc = initialize_pipes(dev);
 	if (rc != EOK) {
@@ -307,5 +445,97 @@
 	}
 
+	(void) initialize_alternate_interfaces(dev);
+
 	return driver->ops->add_device(dev);
+}
+
+/** Destroy existing pipes of a USB device.
+ *
+ * @param dev Device where to destroy the pipes.
+ * @return Error code.
+ */
+static int destroy_current_pipes(usb_device_t *dev)
+{
+	size_t i;
+	int rc;
+
+	/* TODO: this shall be done under some device mutex. */
+
+	/* First check that no session is opened. */
+	for (i = 0; i < dev->pipes_count; i++) {
+		if (usb_pipe_is_session_started(dev->pipes[i].pipe)) {
+			return EBUSY;
+		}
+	}
+
+	/* Prepare connection to HC. */
+	usb_hc_connection_t hc_conn;
+	rc = usb_hc_connection_initialize_from_device(&hc_conn, dev->ddf_dev);
+	if (rc != EOK) {
+		return rc;
+	}
+	rc = usb_hc_connection_open(&hc_conn);
+	if (rc != EOK) {
+		return rc;
+	}
+
+	/* Destroy the pipes. */
+	for (i = 0; i < dev->pipes_count; i++) {
+		usb_pipe_unregister(dev->pipes[i].pipe, &hc_conn);
+		free(dev->pipes[i].pipe);
+	}
+
+	usb_hc_connection_close(&hc_conn);
+
+	free(dev->pipes);
+	dev->pipes = NULL;
+	dev->pipes_count = 0;
+
+	return EOK;
+}
+
+/** Change interface setting of a device.
+ * This function selects new alternate setting of an interface by issuing
+ * proper USB command to the device and also creates new USB pipes
+ * under @c dev->pipes.
+ *
+ * @warning This function is intended for drivers working at interface level.
+ * For drivers controlling the whole device, you need to change interface
+ * manually using usb_request_set_interface() and creating new pipes
+ * with usb_pipe_initialize_from_configuration().
+ *
+ * @param dev USB device.
+ * @param alternate_setting Alternate setting to choose.
+ * @param endpoints New endpoint descriptions.
+ * @return Error code.
+ */
+int usb_device_select_interface(usb_device_t *dev, uint8_t alternate_setting,
+    usb_endpoint_description_t **endpoints)
+{
+	if (dev->interface_no < 0) {
+		return EINVAL;
+	}
+
+	int rc;
+
+	/* TODO: more transactional behavior. */
+
+	/* Destroy existing pipes. */
+	rc = destroy_current_pipes(dev);
+	if (rc != EOK) {
+		return rc;
+	}
+
+	/* Change the interface itself. */
+	rc = usb_request_set_interface(&dev->ctrl_pipe, dev->interface_no,
+	    alternate_setting);
+	if (rc != EOK) {
+		return rc;
+	}
+
+	/* Create new pipes. */
+	rc = initialize_other_pipes(endpoints, dev);
+
+	return rc;
 }
 
Index: uspace/lib/usb/src/host/batch.c
===================================================================
--- uspace/lib/usb/src/host/batch.c	(revision 15a9e63987fa5c5f92fa640a7f17336dab5c09c1)
+++ uspace/lib/usb/src/host/batch.c	(revision f8e8738d31d2b92e00a460fe812bda69bfb6ef51)
@@ -54,4 +54,5 @@
     void *arg,
     ddf_fun_t *fun,
+		endpoint_t *ep,
     void *private_data
     )
@@ -77,5 +78,5 @@
 	instance->next_step = NULL;
 	instance->error = EOK;
-
+	instance->ep = ep;
 }
 /*----------------------------------------------------------------------------*/
Index: uspace/lib/usb/src/host/device_keeper.c
===================================================================
--- uspace/lib/usb/src/host/device_keeper.c	(revision 15a9e63987fa5c5f92fa640a7f17336dab5c09c1)
+++ uspace/lib/usb/src/host/device_keeper.c	(revision f8e8738d31d2b92e00a460fe812bda69bfb6ef51)
@@ -54,9 +54,18 @@
 	for (; i < USB_ADDRESS_COUNT; ++i) {
 		instance->devices[i].occupied = false;
-		instance->devices[i].control_used = false;
+		instance->devices[i].control_used = 0;
 		instance->devices[i].handle = 0;
-		instance->devices[i].toggle_status[0] = 0;
-		instance->devices[i].toggle_status[1] = 0;
-	}
+		list_initialize(&instance->devices[i].endpoints);
+	}
+}
+/*----------------------------------------------------------------------------*/
+void usb_device_keeper_add_ep(
+    usb_device_keeper_t *instance, usb_address_t address, endpoint_t *ep)
+{
+	assert(instance);
+	fibril_mutex_lock(&instance->guard);
+	assert(instance->devices[address].occupied);
+	list_append(&ep->same_device_eps, &instance->devices[address].endpoints);
+	fibril_mutex_unlock(&instance->guard);
 }
 /*----------------------------------------------------------------------------*/
@@ -66,6 +75,6 @@
  * @param[in] speed Speed of the device requesting default address.
  */
-void usb_device_keeper_reserve_default_address(usb_device_keeper_t *instance,
-    usb_speed_t speed)
+void usb_device_keeper_reserve_default_address(
+    usb_device_keeper_t *instance, usb_speed_t speed)
 {
 	assert(instance);
@@ -101,6 +110,6 @@
  * Really ugly one.
  */
-void usb_device_keeper_reset_if_need(usb_device_keeper_t *instance,
-    usb_target_t target, const uint8_t *data)
+void usb_device_keeper_reset_if_need(
+    usb_device_keeper_t *instance, usb_target_t target, const uint8_t *data)
 {
 	assert(instance);
@@ -119,9 +128,14 @@
 		/* recipient is endpoint, value is zero (ENDPOINT_STALL) */
 		if (((data[0] & 0xf) == 1) && ((data[2] | data[3]) == 0)) {
+			link_t *current =
+			    instance->devices[target.address].endpoints.next;
+			while (current !=
+			   &instance->devices[target.address].endpoints)
+			{
 			/* endpoint number is < 16, thus first byte is enough */
-			instance->devices[target.address].toggle_status[0] &=
-			    ~(1 << data[4]);
-			instance->devices[target.address].toggle_status[1] &=
-			    ~(1 << data[4]);
+				endpoint_toggle_reset_filtered(
+				    current, data[4]);
+				current = current->next;
+			}
 		}
 	break;
@@ -131,73 +145,16 @@
 		/* target must be device */
 		if ((data[0] & 0xf) == 0) {
-			instance->devices[target.address].toggle_status[0] = 0;
-			instance->devices[target.address].toggle_status[1] = 0;
+			link_t *current =
+			    instance->devices[target.address].endpoints.next;
+			while (current !=
+			   &instance->devices[target.address].endpoints)
+			{
+				endpoint_toggle_reset(current);
+				current = current->next;
+			}
 		}
 	break;
 	}
 	fibril_mutex_unlock(&instance->guard);
-}
-/*----------------------------------------------------------------------------*/
-/** Get current value of endpoint toggle.
- *
- * @param[in] instance Device keeper structure to use.
- * @param[in] target Device and endpoint used.
- * @return Error code
- */
-int usb_device_keeper_get_toggle(usb_device_keeper_t *instance,
-    usb_target_t target, usb_direction_t direction)
-{
-	assert(instance);
-	/* only control pipes are bi-directional and those do not need toggle */
-	if (direction == USB_DIRECTION_BOTH)
-		return ENOENT;
-	int ret;
-	fibril_mutex_lock(&instance->guard);
-	if (target.endpoint > 15 || target.endpoint < 0
-	    || target.address >= USB_ADDRESS_COUNT || target.address < 0
-	    || !instance->devices[target.address].occupied) {
-		usb_log_error("Invalid data when asking for toggle value.\n");
-		ret = EINVAL;
-	} else {
-		ret = (instance->devices[target.address].toggle_status[direction]
-		        >> target.endpoint) & 1;
-	}
-	fibril_mutex_unlock(&instance->guard);
-	return ret;
-}
-/*----------------------------------------------------------------------------*/
-/** Set current value of endpoint toggle.
- *
- * @param[in] instance Device keeper structure to use.
- * @param[in] target Device and endpoint used.
- * @param[in] toggle Toggle value.
- * @return Error code.
- */
-int usb_device_keeper_set_toggle(usb_device_keeper_t *instance,
-    usb_target_t target, usb_direction_t direction, bool toggle)
-{
-	assert(instance);
-	/* only control pipes are bi-directional and those do not need toggle */
-	if (direction == USB_DIRECTION_BOTH)
-		return ENOENT;
-	int ret;
-	fibril_mutex_lock(&instance->guard);
-	if (target.endpoint > 15 || target.endpoint < 0
-	    || target.address >= USB_ADDRESS_COUNT || target.address < 0
-	    || !instance->devices[target.address].occupied) {
-		usb_log_error("Invalid data when setting toggle value.\n");
-		ret = EINVAL;
-	} else {
-		if (toggle) {
-			instance->devices[target.address].toggle_status[direction]
-			    |= (1 << target.endpoint);
-		} else {
-			instance->devices[target.address].toggle_status[direction]
-			    &= ~(1 << target.endpoint);
-		}
-		ret = EOK;
-	}
-	fibril_mutex_unlock(&instance->guard);
-	return ret;
 }
 /*----------------------------------------------------------------------------*/
@@ -208,6 +165,6 @@
  * @return Free address, or error code.
  */
-usb_address_t device_keeper_get_free_address(usb_device_keeper_t *instance,
-    usb_speed_t speed)
+usb_address_t device_keeper_get_free_address(
+    usb_device_keeper_t *instance, usb_speed_t speed)
 {
 	assert(instance);
@@ -229,6 +186,4 @@
 	instance->devices[new_address].occupied = true;
 	instance->devices[new_address].speed = speed;
-	instance->devices[new_address].toggle_status[0] = 0;
-	instance->devices[new_address].toggle_status[1] = 0;
 	instance->last_address = new_address;
 	fibril_mutex_unlock(&instance->guard);
@@ -259,6 +214,6 @@
  * @param[in] address Device address
  */
-void usb_device_keeper_release(usb_device_keeper_t *instance,
-    usb_address_t address)
+void usb_device_keeper_release(
+    usb_device_keeper_t *instance, usb_address_t address)
 {
 	assert(instance);
@@ -278,6 +233,6 @@
  * @return USB Address, or error code.
  */
-usb_address_t usb_device_keeper_find(usb_device_keeper_t *instance,
-    devman_handle_t handle)
+usb_address_t usb_device_keeper_find(
+    usb_device_keeper_t *instance, devman_handle_t handle)
 {
 	assert(instance);
@@ -301,6 +256,6 @@
  * @return USB speed.
  */
-usb_speed_t usb_device_keeper_get_speed(usb_device_keeper_t *instance,
-    usb_address_t address)
+usb_speed_t usb_device_keeper_get_speed(
+    usb_device_keeper_t *instance, usb_address_t address)
 {
 	assert(instance);
@@ -310,22 +265,25 @@
 }
 /*----------------------------------------------------------------------------*/
-void usb_device_keeper_use_control(usb_device_keeper_t *instance,
-    usb_address_t address)
-{
-	assert(instance);
-	fibril_mutex_lock(&instance->guard);
-	while (instance->devices[address].control_used) {
+void usb_device_keeper_use_control(
+    usb_device_keeper_t *instance, usb_target_t target)
+{
+	assert(instance);
+	const uint16_t ep = 1 << target.endpoint;
+	fibril_mutex_lock(&instance->guard);
+	while (instance->devices[target.address].control_used & ep) {
 		fibril_condvar_wait(&instance->change, &instance->guard);
 	}
-	instance->devices[address].control_used = true;
-	fibril_mutex_unlock(&instance->guard);
-}
-/*----------------------------------------------------------------------------*/
-void usb_device_keeper_release_control(usb_device_keeper_t *instance,
-    usb_address_t address)
-{
-	assert(instance);
-	fibril_mutex_lock(&instance->guard);
-	instance->devices[address].control_used = false;
+	instance->devices[target.address].control_used |= ep;
+	fibril_mutex_unlock(&instance->guard);
+}
+/*----------------------------------------------------------------------------*/
+void usb_device_keeper_release_control(
+    usb_device_keeper_t *instance, usb_target_t target)
+{
+	assert(instance);
+	const uint16_t ep = 1 << target.endpoint;
+	fibril_mutex_lock(&instance->guard);
+	assert((instance->devices[target.address].control_used & ep) != 0);
+	instance->devices[target.address].control_used &= ~ep;
 	fibril_mutex_unlock(&instance->guard);
 	fibril_condvar_signal(&instance->change);
Index: uspace/lib/usb/src/host/endpoint.c
===================================================================
--- uspace/lib/usb/src/host/endpoint.c	(revision f8e8738d31d2b92e00a460fe812bda69bfb6ef51)
+++ uspace/lib/usb/src/host/endpoint.c	(revision f8e8738d31d2b92e00a460fe812bda69bfb6ef51)
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup drvusbuhcihc
+ * @{
+ */
+/** @file
+ * @brief UHCI host controller driver structure
+ */
+
+#include <errno.h>
+#include <usb/host/endpoint.h>
+
+int endpoint_init(endpoint_t *instance, usb_address_t address,
+    usb_endpoint_t endpoint, usb_direction_t direction,
+    usb_transfer_type_t type, usb_speed_t speed, size_t max_packet_size)
+{
+	assert(instance);
+	instance->address = address;
+	instance->endpoint = endpoint;
+	instance->direction = direction;
+	instance->transfer_type = type;
+	instance->speed = speed;
+	instance->max_packet_size = max_packet_size;
+	instance->toggle = 0;
+	link_initialize(&instance->same_device_eps);
+	return EOK;
+}
+/*----------------------------------------------------------------------------*/
+void endpoint_destroy(endpoint_t *instance)
+{
+	assert(instance);
+	list_remove(&instance->same_device_eps);
+	free(instance);
+}
+/*----------------------------------------------------------------------------*/
+int endpoint_toggle_get(endpoint_t *instance)
+{
+	assert(instance);
+	return (int)instance->toggle;
+}
+/*----------------------------------------------------------------------------*/
+void endpoint_toggle_set(endpoint_t *instance, int toggle)
+{
+	assert(instance);
+	assert(toggle == 0 || toggle == 1);
+	instance->toggle = toggle;
+}
+/*----------------------------------------------------------------------------*/
+void endpoint_toggle_reset(link_t *ep)
+{
+	endpoint_t *instance =
+	    list_get_instance(ep, endpoint_t, same_device_eps);
+	assert(instance);
+	instance->toggle = 0;
+}
+/*----------------------------------------------------------------------------*/
+void endpoint_toggle_reset_filtered(link_t *ep, usb_endpoint_t epn)
+{
+	endpoint_t *instance =
+	    list_get_instance(ep, endpoint_t, same_device_eps);
+	assert(instance);
+	if (instance->endpoint == epn)
+		instance->toggle = 0;
+}
+/**
+ * @}
+ */
Index: uspace/lib/usb/src/host/usb_endpoint_manager.c
===================================================================
--- uspace/lib/usb/src/host/usb_endpoint_manager.c	(revision f8e8738d31d2b92e00a460fe812bda69bfb6ef51)
+++ uspace/lib/usb/src/host/usb_endpoint_manager.c	(revision f8e8738d31d2b92e00a460fe812bda69bfb6ef51)
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2011 Jan Vesely
+ * All rights eps.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <bool.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <usb/host/usb_endpoint_manager.h>
+
+#define BUCKET_COUNT 7
+
+#define MAX_KEYS (3)
+typedef struct {
+	link_t link;
+	size_t bw;
+	endpoint_t *ep;
+} node_t;
+/*----------------------------------------------------------------------------*/
+static hash_index_t node_hash(unsigned long key[])
+{
+	hash_index_t hash = 0;
+	unsigned i = 0;
+	for (;i < MAX_KEYS; ++i) {
+		hash ^= key[i];
+	}
+	hash %= BUCKET_COUNT;
+	return hash;
+}
+/*----------------------------------------------------------------------------*/
+static int node_compare(unsigned long key[], hash_count_t keys, link_t *item)
+{
+	assert(item);
+	node_t *node = hash_table_get_instance(item, node_t, link);
+	assert(node);
+	assert(node->ep);
+	bool match = true;
+	switch (keys) {
+	case 3:
+		match = match && (key[2] == node->ep->direction);
+	case 2:
+		match = match && (key[1] == (unsigned long)node->ep->endpoint);
+	case 1:
+		match = match && (key[0] == (unsigned long)node->ep->address);
+		break;
+	default:
+		match = false;
+	}
+	return match;
+}
+/*----------------------------------------------------------------------------*/
+static void node_remove(link_t *item)
+{
+	assert(item);
+	node_t *node = hash_table_get_instance(item, node_t, link);
+	endpoint_destroy(node->ep);
+	free(node);
+}
+/*----------------------------------------------------------------------------*/
+static hash_table_operations_t op = {
+	.hash = node_hash,
+	.compare = node_compare,
+	.remove_callback = node_remove,
+};
+/*----------------------------------------------------------------------------*/
+size_t bandwidth_count_usb11(usb_speed_t speed, usb_transfer_type_t type,
+    size_t size, size_t max_packet_size)
+{
+	/* We care about bandwidth only for interrupt and isochronous. */
+	if ((type != USB_TRANSFER_INTERRUPT)
+	    && (type != USB_TRANSFER_ISOCHRONOUS)) {
+		return 0;
+	}
+
+	const unsigned packet_count =
+	    (size + max_packet_size - 1) / max_packet_size;
+	/* TODO: It may be that ISO and INT transfers use only one data packet
+	 * per transaction, but I did not find text in UB spec that confirms
+	 * this */
+	/* NOTE: All data packets will be considered to be max_packet_size */
+	switch (speed)
+	{
+	case USB_SPEED_LOW:
+		assert(type == USB_TRANSFER_INTERRUPT);
+		/* Protocol overhead 13B
+		 * (3 SYNC bytes, 3 PID bytes, 2 Endpoint + CRC bytes, 2
+		 * CRC bytes, and a 3-byte interpacket delay)
+		 * see USB spec page 45-46. */
+		/* Speed penalty 8: low speed is 8-times slower*/
+		return packet_count * (13 + max_packet_size) * 8;
+	case USB_SPEED_FULL:
+		/* Interrupt transfer overhead see above
+		 * or page 45 of USB spec */
+		if (type == USB_TRANSFER_INTERRUPT)
+			return packet_count * (13 + max_packet_size);
+
+		assert(type == USB_TRANSFER_ISOCHRONOUS);
+		/* Protocol overhead 9B
+		 * (2 SYNC bytes, 2 PID bytes, 2 Endpoint + CRC bytes, 2 CRC
+		 * bytes, and a 1-byte interpacket delay)
+		 * see USB spec page 42 */
+		return packet_count * (9 + max_packet_size);
+	default:
+		return 0;
+	}
+}
+/*----------------------------------------------------------------------------*/
+int usb_endpoint_manager_init(usb_endpoint_manager_t *instance,
+    size_t available_bandwidth)
+{
+	assert(instance);
+	fibril_mutex_initialize(&instance->guard);
+	fibril_condvar_initialize(&instance->change);
+	instance->free_bw = available_bandwidth;
+	bool ht =
+	    hash_table_create(&instance->ep_table, BUCKET_COUNT, MAX_KEYS, &op);
+	return ht ? EOK : ENOMEM;
+}
+/*----------------------------------------------------------------------------*/
+void usb_endpoint_manager_destroy(usb_endpoint_manager_t *instance)
+{
+	hash_table_destroy(&instance->ep_table);
+}
+/*----------------------------------------------------------------------------*/
+int usb_endpoint_manager_register_ep(usb_endpoint_manager_t *instance,
+    endpoint_t *ep, size_t data_size)
+{
+	assert(ep);
+	size_t bw = bandwidth_count_usb11(ep->speed, ep->transfer_type,
+	    data_size, ep->max_packet_size);
+	assert(instance);
+
+	unsigned long key[MAX_KEYS] =
+	    {ep->address, ep->endpoint, ep->direction};
+	fibril_mutex_lock(&instance->guard);
+
+	link_t *item =
+	    hash_table_find(&instance->ep_table, key);
+	if (item != NULL) {
+		fibril_mutex_unlock(&instance->guard);
+		return EEXISTS;
+	}
+
+	if (bw > instance->free_bw) {
+		fibril_mutex_unlock(&instance->guard);
+		return ENOSPC;
+	}
+
+	node_t *node = malloc(sizeof(node_t));
+	if (node == NULL) {
+		fibril_mutex_unlock(&instance->guard);
+		return ENOMEM;
+	}
+
+	node->bw = bw;
+	node->ep = ep;
+	link_initialize(&node->link);
+
+	hash_table_insert(&instance->ep_table, key, &node->link);
+	instance->free_bw -= bw;
+	fibril_mutex_unlock(&instance->guard);
+	fibril_condvar_broadcast(&instance->change);
+	return EOK;
+}
+/*----------------------------------------------------------------------------*/
+int usb_endpoint_manager_unregister_ep(usb_endpoint_manager_t *instance,
+    usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction)
+{
+	assert(instance);
+	unsigned long key[MAX_KEYS] = {address, endpoint, direction};
+
+	fibril_mutex_lock(&instance->guard);
+	link_t *item = hash_table_find(&instance->ep_table, key);
+	if (item == NULL) {
+		fibril_mutex_unlock(&instance->guard);
+		return EINVAL;
+	}
+
+	node_t *node = hash_table_get_instance(item, node_t, link);
+	instance->free_bw += node->bw;
+	hash_table_remove(&instance->ep_table, key, MAX_KEYS);
+
+	fibril_mutex_unlock(&instance->guard);
+	fibril_condvar_broadcast(&instance->change);
+	return EOK;
+}
+/*----------------------------------------------------------------------------*/
+endpoint_t * usb_endpoint_manager_get_ep(usb_endpoint_manager_t *instance,
+    usb_address_t address, usb_endpoint_t endpoint, usb_direction_t direction,
+    size_t *bw)
+{
+	assert(instance);
+	unsigned long key[MAX_KEYS] = {address, endpoint, direction};
+
+	fibril_mutex_lock(&instance->guard);
+	link_t *item = hash_table_find(&instance->ep_table, key);
+	if (item == NULL) {
+		fibril_mutex_unlock(&instance->guard);
+		return NULL;
+	}
+	node_t *node = hash_table_get_instance(item, node_t, link);
+	if (bw)
+		*bw = node->bw;
+
+	fibril_mutex_unlock(&instance->guard);
+	return node->ep;
+}
Index: uspace/lib/usb/src/hub.c
===================================================================
--- uspace/lib/usb/src/hub.c	(revision 15a9e63987fa5c5f92fa640a7f17336dab5c09c1)
+++ uspace/lib/usb/src/hub.c	(revision f8e8738d31d2b92e00a460fe812bda69bfb6ef51)
@@ -142,4 +142,23 @@
 	    DEV_IFACE_ID(USBHC_DEV_IFACE),
 	    IPC_M_USBHC_RELEASE_ADDRESS, address);
+}
+
+static void unregister_control_endpoint_on_default_address(
+    usb_hc_connection_t *connection)
+{
+	usb_device_connection_t dev_conn;
+	int rc = usb_device_connection_initialize_on_default_address(&dev_conn,
+	    connection);
+	if (rc != EOK) {
+		return;
+	}
+
+	usb_pipe_t ctrl_pipe;
+	rc = usb_pipe_initialize_default_control(&ctrl_pipe, &dev_conn);
+	if (rc != EOK) {
+		return;
+	}
+
+	usb_pipe_unregister(&ctrl_pipe, connection);
 }
 
@@ -235,4 +254,13 @@
 		goto leave_release_default_address;
 	}
+
+	/* Before sending any traffic, we need to register this
+	 * endpoint.
+	 */
+	rc = usb_pipe_register(&ctrl_pipe, 0, connection);
+	if (rc != EOK) {
+		rc = EREFUSED;
+		goto leave_release_default_address;
+	}
 	rc = usb_pipe_probe_default_control(&ctrl_pipe);
 	if (rc != EOK) {
@@ -244,5 +272,5 @@
 	if (rc != EOK) {
 		rc = ENOTCONN;
-		goto leave_release_default_address;
+		goto leave_unregister_endpoint;
 	}
 
@@ -256,7 +284,22 @@
 
 	/*
+	 * Register the control endpoint for the new device.
+	 */
+	rc = usb_pipe_register(&ctrl_pipe, 0, connection);
+	if (rc != EOK) {
+		rc = EREFUSED;
+		goto leave_unregister_endpoint;
+	}
+
+	/*
+	 * Release the original endpoint.
+	 */
+	unregister_control_endpoint_on_default_address(connection);
+
+	/*
 	 * Once the address is changed, we can return the default address.
 	 */
 	usb_hc_release_default_address(connection);
+
 
 	/*
@@ -273,4 +316,6 @@
 	}
 
+
+
 	/*
 	 * And now inform the host controller about the handle.
@@ -308,4 +353,7 @@
 	usb_pipe_end_session(&ctrl_pipe);
 
+leave_unregister_endpoint:
+	usb_pipe_unregister(&ctrl_pipe, connection);
+
 leave_release_default_address:
 	usb_hc_release_default_address(connection);
Index: uspace/lib/usb/src/pipesinit.c
===================================================================
--- uspace/lib/usb/src/pipesinit.c	(revision 15a9e63987fa5c5f92fa640a7f17336dab5c09c1)
+++ uspace/lib/usb/src/pipesinit.c	(revision f8e8738d31d2b92e00a460fe812bda69bfb6ef51)
@@ -121,5 +121,5 @@
     usb_endpoint_mapping_t *mapping, size_t mapping_count,
     usb_endpoint_description_t *found_endpoint,
-    int interface_number)
+    int interface_number, int interface_setting)
 {
 	while (mapping_count > 0) {
@@ -127,8 +127,13 @@
 		    || (mapping->interface_no == interface_number);
 
+		bool interface_setting_fits = (mapping->interface_setting < 0)
+		    || (mapping->interface_setting == interface_setting);
+
 		bool endpoint_descriptions_fits = endpoint_fits_description(
 		    mapping->description, found_endpoint);
 
-		if (interface_number_fits && endpoint_descriptions_fits) {
+		if (interface_number_fits
+		    && interface_setting_fits
+		    && endpoint_descriptions_fits) {
 			return mapping;
 		}
@@ -181,5 +186,6 @@
 	 */
 	usb_endpoint_mapping_t *ep_mapping = find_endpoint_mapping(mapping,
-	    mapping_count, &description, interface->interface_number);
+	    mapping_count, &description,
+	    interface->interface_number, interface->alternate_setting);
 	if (ep_mapping == NULL) {
 		return ENOENT;
Index: uspace/lib/usb/src/request.c
===================================================================
--- uspace/lib/usb/src/request.c	(revision 15a9e63987fa5c5f92fa640a7f17336dab5c09c1)
+++ uspace/lib/usb/src/request.c	(revision f8e8738d31d2b92e00a460fe812bda69bfb6ef51)
@@ -529,5 +529,4 @@
 		return rc;
 	}
-
 	if (bare_config.descriptor_type != USB_DESCTYPE_CONFIGURATION) {
 		return ENOENT;
