Index: uspace/drv/vhc/connhost.c
===================================================================
--- uspace/drv/vhc/connhost.c	(revision 707ffcf9b598647f70b37a330aa42283df4f96be)
+++ uspace/drv/vhc/connhost.c	(revision 0a37e1494803f66c6c50cf130b55944ace37c64c)
@@ -42,4 +42,5 @@
 #include "hc.h"
 
+
 typedef struct {
 	usb_direction_t direction;
@@ -47,11 +48,27 @@
 	usbhc_iface_transfer_in_callback_t in_callback;
 	device_t *dev;
+	size_t reported_size;
 	void *arg;
 } transfer_info_t;
 
+typedef struct {
+	usb_direction_t direction;
+	usb_target_t target;
+	usbhc_iface_transfer_out_callback_t out_callback;
+	usbhc_iface_transfer_in_callback_t in_callback;
+	device_t *dev;
+	void *arg;
+	void *data_buffer;
+	size_t data_buffer_size;
+} control_transfer_info_t;
+
 static void universal_callback(void *buffer, size_t size,
     usb_transaction_outcome_t outcome, void *arg)
 {
 	transfer_info_t *transfer = (transfer_info_t *) arg;
+
+	if (transfer->reported_size != (size_t) -1) {
+		size = transfer->reported_size;
+	}
 
 	switch (transfer->direction) {
@@ -84,4 +101,113 @@
 	transfer->arg = arg;
 	transfer->dev = dev;
+	transfer->reported_size = (size_t) -1;
+
+	return transfer;
+}
+
+static void control_abort_prematurely(control_transfer_info_t *transfer,
+    size_t size, usb_transaction_outcome_t outcome)
+{
+	switch (transfer->direction) {
+		case USB_DIRECTION_IN:
+			transfer->in_callback(transfer->dev,
+			    outcome, size,
+			    transfer->arg);
+			break;
+		case USB_DIRECTION_OUT:
+			transfer->out_callback(transfer->dev,
+			    outcome,
+			    transfer->arg);
+			break;
+		default:
+			assert(false && "unreachable");
+			break;
+	}
+}
+
+static void control_callback_two(void *buffer, size_t size,
+    usb_transaction_outcome_t outcome, void *arg)
+{
+	control_transfer_info_t *ctrl_transfer = (control_transfer_info_t *) arg;
+
+	if (outcome != USB_OUTCOME_OK) {
+		control_abort_prematurely(ctrl_transfer, outcome, size);
+		free(ctrl_transfer);
+		return;
+	}
+
+	transfer_info_t *transfer  = create_transfer_info(ctrl_transfer->dev,
+	    ctrl_transfer->direction, ctrl_transfer->arg);
+	transfer->out_callback = ctrl_transfer->out_callback;
+	transfer->in_callback = ctrl_transfer->in_callback;
+	transfer->reported_size = size;
+
+	switch (ctrl_transfer->direction) {
+		case USB_DIRECTION_IN:
+			hc_add_transaction_to_device(false, ctrl_transfer->target,
+			    USB_TRANSFER_CONTROL,
+			    NULL, 0,
+			    universal_callback, transfer);
+			break;
+		case USB_DIRECTION_OUT:
+			hc_add_transaction_from_device(ctrl_transfer->target,
+			    USB_TRANSFER_CONTROL,
+			    NULL, 0,
+			    universal_callback, transfer);
+			break;
+		default:
+			assert(false && "unreachable");
+			break;
+	}
+
+	free(ctrl_transfer);
+}
+
+static void control_callback_one(void *buffer, size_t size,
+    usb_transaction_outcome_t outcome, void *arg)
+{
+	control_transfer_info_t *transfer = (control_transfer_info_t *) arg;
+
+	if (outcome != USB_OUTCOME_OK) {
+		control_abort_prematurely(transfer, outcome, size);
+		free(transfer);
+		return;
+	}
+
+	switch (transfer->direction) {
+		case USB_DIRECTION_IN:
+			hc_add_transaction_from_device(transfer->target,
+			    USB_TRANSFER_CONTROL,
+			    transfer->data_buffer, transfer->data_buffer_size,
+			    control_callback_two, transfer);
+			break;
+		case USB_DIRECTION_OUT:
+			hc_add_transaction_to_device(false, transfer->target,
+			    USB_TRANSFER_CONTROL,
+			    transfer->data_buffer, transfer->data_buffer_size,
+			    control_callback_two, transfer);
+			break;
+		default:
+			assert(false && "unreachable");
+			break;
+	}
+}
+
+static control_transfer_info_t *create_control_transfer_info(device_t *dev,
+    usb_direction_t direction, usb_target_t target,
+    void *data_buffer, size_t data_buffer_size,
+    void *arg)
+{
+	control_transfer_info_t *transfer
+	    = malloc(sizeof(control_transfer_info_t));
+
+	transfer->direction = direction;
+	transfer->target = target;
+	transfer->in_callback = NULL;
+	transfer->out_callback = NULL;
+	transfer->arg = arg;
+	transfer->dev = dev;
+	transfer->data_buffer = data_buffer;
+	transfer->data_buffer_size = data_buffer_size;
 
 	return transfer;
@@ -193,4 +319,21 @@
 }
 
+static int control_write(device_t *dev, usb_target_t target,
+    void *setup_packet, size_t setup_packet_size,
+    void *data, size_t data_size,
+    usbhc_iface_transfer_out_callback_t callback, void *arg)
+{
+	control_transfer_info_t *transfer
+	    = create_control_transfer_info(dev, USB_DIRECTION_OUT, target,
+	    data, data_size, arg);
+	transfer->out_callback = callback;
+
+	hc_add_transaction_to_device(true, target, USB_TRANSFER_CONTROL,
+	    setup_packet, setup_packet_size,
+	    control_callback_one, transfer);
+
+	return EOK;
+}
+
 static int control_read_setup(device_t *dev, usb_target_t target,
     void *data, size_t size,
@@ -217,4 +360,21 @@
 	    NULL, 0,
 	    callback, arg);
+}
+
+static int control_read(device_t *dev, usb_target_t target,
+    void *setup_packet, size_t setup_packet_size,
+    void *data, size_t data_size,
+    usbhc_iface_transfer_in_callback_t callback, void *arg)
+{
+	control_transfer_info_t *transfer
+	    = create_control_transfer_info(dev, USB_DIRECTION_IN, target,
+	    data, data_size, arg);
+	transfer->in_callback = callback;
+
+	hc_add_transaction_to_device(true, target, USB_TRANSFER_CONTROL,
+	    setup_packet, setup_packet_size,
+	    control_callback_one, transfer);
+
+	return EOK;
 }
 
@@ -290,7 +450,11 @@
 	.control_write_status = control_write_status,
 
+	.control_write = control_write,
+
 	.control_read_setup = control_read_setup,
 	.control_read_data = control_read_data,
-	.control_read_status = control_read_status
+	.control_read_status = control_read_status,
+
+	.control_read = control_read
 };
 
