Index: uspace/lib/drv/generic/remote_usbhc.c
===================================================================
--- uspace/lib/drv/generic/remote_usbhc.c	(revision 3b77628b520eb4eb2108181a33848556e4d06e36)
+++ uspace/lib/drv/generic/remote_usbhc.c	(revision fe4dd14b607dfc4f86f5c66da2dc991b70531dd3)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2010 Vojtech Horky
+ * Copyright (c) 2010-2011 Vojtech Horky
  * All rights reserved.
  *
@@ -52,4 +52,6 @@
 static void remote_usbhc_control_read_data(device_t *, void *, ipc_callid_t, ipc_call_t *);
 static void remote_usbhc_control_read_status(device_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_usbhc_control_write(device_t *, void *, ipc_callid_t, ipc_call_t *);
+static void remote_usbhc_control_read(device_t *, void *, ipc_callid_t, ipc_call_t *);
 static void remote_usbhc_reserve_default_address(device_t *, void *, ipc_callid_t, ipc_call_t *);
 static void remote_usbhc_release_default_address(device_t *, void *, ipc_callid_t, ipc_call_t *);
@@ -81,5 +83,8 @@
 	remote_usbhc_control_read_setup,
 	remote_usbhc_control_read_data,
-	remote_usbhc_control_read_status
+	remote_usbhc_control_read_status,
+
+	remote_usbhc_control_write,
+	remote_usbhc_control_read
 };
 
@@ -95,4 +100,5 @@
 	ipc_callid_t caller;
 	void *buffer;
+	void *setup_packet;
 	size_t size;
 } async_transaction_t;
@@ -297,4 +303,5 @@
 	trans->caller = callid;
 	trans->buffer = buffer;
+	trans->setup_packet = NULL;
 	trans->size = len;
 
@@ -336,4 +343,5 @@
 	trans->caller = callid;
 	trans->buffer = malloc(len);
+	trans->setup_packet = NULL;
 	trans->size = len;
 
@@ -391,4 +399,5 @@
 	trans->caller = callid;
 	trans->buffer = NULL;
+	trans->setup_packet = NULL;
 	trans->size = 0;
 
@@ -496,4 +505,109 @@
 }
 
+void remote_usbhc_control_write(device_t *device, void *iface,
+ipc_callid_t callid, ipc_call_t *call)
+{
+	usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
+	assert(usb_iface != NULL);
+
+	if (!usb_iface->control_write) {
+		ipc_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	usb_target_t target = {
+		.address = DEV_IPC_GET_ARG1(*call),
+		.endpoint = DEV_IPC_GET_ARG2(*call)
+	};
+
+	int rc;
+
+	void *setup_packet = NULL;
+	void *data_buffer = NULL;
+	size_t setup_packet_len = 0;
+	size_t data_buffer_len = 0;
+
+	rc = async_data_write_accept(&setup_packet, false,
+	    1, USB_MAX_PAYLOAD_SIZE, 0, &setup_packet_len);
+	if (rc != EOK) {
+		ipc_answer_0(callid, rc);
+		return;
+	}
+	rc = async_data_write_accept(&data_buffer, false,
+	    1, USB_MAX_PAYLOAD_SIZE, 0, &data_buffer_len);
+	if (rc != EOK) {
+		free(setup_packet);
+		ipc_answer_0(callid, rc);
+		return;
+	}
+
+	async_transaction_t *trans = malloc(sizeof(async_transaction_t));
+	trans->caller = callid;
+	trans->setup_packet = setup_packet;
+	trans->buffer = data_buffer;
+	trans->size = data_buffer_len;
+
+	rc = usb_iface->control_write(device, target,
+	    setup_packet, setup_packet_len,
+	    data_buffer, data_buffer_len,
+	    callback_out, trans);
+
+	if (rc != EOK) {
+		ipc_answer_0(callid, rc);
+		free(setup_packet);
+		free(data_buffer);
+		free(trans);
+	}
+}
+
+
+void remote_usbhc_control_read(device_t *device, void *iface,
+ipc_callid_t callid, ipc_call_t *call)
+{
+	usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
+	assert(usb_iface != NULL);
+
+	if (!usb_iface->control_read) {
+		ipc_answer_0(callid, ENOTSUP);
+		return;
+	}
+
+	size_t data_len = DEV_IPC_GET_ARG3(*call);
+	usb_target_t target = {
+		.address = DEV_IPC_GET_ARG1(*call),
+		.endpoint = DEV_IPC_GET_ARG2(*call)
+	};
+
+	int rc;
+
+	void *setup_packet = NULL;
+	size_t setup_packet_len = 0;
+
+	rc = async_data_write_accept(&setup_packet, false,
+	    1, USB_MAX_PAYLOAD_SIZE, 0, &setup_packet_len);
+	if (rc != EOK) {
+		ipc_answer_0(callid, rc);
+		return;
+	}
+
+	async_transaction_t *trans = malloc(sizeof(async_transaction_t));
+	trans->caller = callid;
+	trans->setup_packet = setup_packet;
+	trans->buffer = malloc(data_len);
+	trans->size = data_len;
+
+	rc = usb_iface->control_read(device, target,
+	    setup_packet, setup_packet_len,
+	    trans->buffer, trans->size,
+	    callback_in, trans);
+
+	if (rc != EOK) {
+		ipc_answer_0(callid, rc);
+		free(setup_packet);
+		free(trans->buffer);
+		free(trans);
+	}
+}
+
 
 
Index: uspace/lib/drv/include/usbhc_iface.h
===================================================================
--- uspace/lib/drv/include/usbhc_iface.h	(revision 3b77628b520eb4eb2108181a33848556e4d06e36)
+++ uspace/lib/drv/include/usbhc_iface.h	(revision fe4dd14b607dfc4f86f5c66da2dc991b70531dd3)
@@ -201,4 +201,20 @@
 	IPC_M_USBHC_CONTROL_READ_STATUS,
 
+	/** Issue control WRITE transfer.
+	 * See explanation at usb_iface_funcs_t (OUT transaction) for
+	 * call parameters.
+	 * This call is immediately followed by two IPC data writes
+	 * from the caller (setup packet and actual data).
+	 */
+	IPC_M_USBHC_CONTROL_WRITE,
+
+	/** Issue control WRITE transfer.
+	 * See explanation at usb_iface_funcs_t (IN transaction) for
+	 * call parameters.
+	 * This call is immediately followed by IPC data read from the caller
+	 * (setup packet).
+	 * Actual data are retrieved through IPC_M_USBHC_GET_BUFFER.
+	 */
+	IPC_M_USBHC_CONTROL_READ,
 
 	/* IPC_M_USB_ */
@@ -249,4 +265,12 @@
 	int (*control_read_status)(device_t *, usb_target_t,
 	    usbhc_iface_transfer_out_callback_t, void *);
+
+	int (*control_write)(device_t *, usb_target_t,
+	    void *, size_t, void *, size_t,
+	    usbhc_iface_transfer_out_callback_t, void *);
+
+	int (*control_read)(device_t *, usb_target_t,
+	    void *, size_t, void *, size_t,
+	    usbhc_iface_transfer_in_callback_t, void *);
 } usbhc_iface_t;
 
