Index: uspace/lib/drv/generic/remote_usbhc.c
===================================================================
--- uspace/lib/drv/generic/remote_usbhc.c	(revision a3dfb2eaf9ecfa290c1a29eddd807f1d30db1583)
+++ uspace/lib/drv/generic/remote_usbhc.c	(revision 43178271ecca79c7c77563dbd2a252faa9b39f82)
@@ -159,8 +159,19 @@
 }
 
-void remote_usbhc_interrupt_out(device_t *device, void *iface,
-	    ipc_callid_t callid, ipc_call_t *call)
-{
-	usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
+/** Process an outgoing transfer (both OUT and SETUP).
+ *
+ * @param device Target device.
+ * @param callid Initiating caller.
+ * @param call Initiating call.
+ * @param transfer_func Transfer function (might be NULL).
+ */
+static void remote_usbhc_out_transfer(device_t *device,
+    ipc_callid_t callid, ipc_call_t *call,
+    usbhc_iface_transfer_out_t transfer_func)
+{
+	if (!transfer_func) {
+		ipc_answer_0(callid, ENOTSUP);
+		return;
+	}
 
 	size_t expected_len = IPC_GET_ARG3(*call);
@@ -183,27 +194,36 @@
 	}
 
-	if (!usb_iface->interrupt_out) {
-		ipc_answer_0(callid, ENOTSUP);
-		return;
-	}
-
 	async_transaction_t *trans = malloc(sizeof(async_transaction_t));
 	trans->caller = callid;
-	trans->buffer = NULL;
-	trans->size = 0;
-
-	int rc = usb_iface->interrupt_out(device, target, buffer, len,
+	trans->buffer = buffer;
+	trans->size = len;
+
+	int rc = transfer_func(device, target, buffer, len,
 	    callback_out, trans);
 
 	if (rc != EOK) {
 		ipc_answer_0(callid, rc);
+		if (buffer != NULL) {
+			free(buffer);
+		}
 		free(trans);
 	}
 }
 
-void remote_usbhc_interrupt_in(device_t *device, void *iface,
-	    ipc_callid_t callid, ipc_call_t *call)
-{
-	usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
+/** Process an incoming transfer.
+ *
+ * @param device Target device.
+ * @param callid Initiating caller.
+ * @param call Initiating call.
+ * @param transfer_func Transfer function (might be NULL).
+ */
+static void remote_usbhc_in_transfer(device_t *device,
+    ipc_callid_t callid, ipc_call_t *call,
+    usbhc_iface_transfer_in_t transfer_func)
+{
+	if (!transfer_func) {
+		ipc_answer_0(callid, ENOTSUP);
+		return;
+	}
 
 	size_t len = IPC_GET_ARG3(*call);
@@ -213,9 +233,4 @@
 	};
 
-	if (!usb_iface->interrupt_in) {
-		ipc_answer_0(callid, ENOTSUP);
-		return;
-	}
-
 	async_transaction_t *trans = malloc(sizeof(async_transaction_t));
 	trans->caller = callid;
@@ -223,5 +238,5 @@
 	trans->size = len;
 
-	int rc = usb_iface->interrupt_in(device, target, trans->buffer, len,
+	int rc = transfer_func(device, target, trans->buffer, len,
 	    callback_in, trans);
 
@@ -233,10 +248,39 @@
 }
 
-void remote_usbhc_control_write_setup(device_t *device, void *iface,
-	    ipc_callid_t callid, ipc_call_t *call)
-{
-	usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
-
-	size_t expected_len = IPC_GET_ARG3(*call);
+/** Process status part of control transfer.
+ *
+ * @param device Target device.
+ * @param callid Initiating caller.
+ * @param call Initiating call.
+ * @param direction Transfer direction (read ~ in, write ~ out).
+ * @param transfer_in_func Transfer function for control read (might be NULL).
+ * @param transfer_out_func Transfer function for control write (might be NULL).
+ */
+static void remote_usbhc_status_transfer(device_t *device,
+    ipc_callid_t callid, ipc_call_t *call,
+    usb_direction_t direction,
+    int (*transfer_in_func)(device_t *, usb_target_t,
+        usbhc_iface_transfer_in_callback_t, void *),
+    int (*transfer_out_func)(device_t *, usb_target_t,
+        usbhc_iface_transfer_out_callback_t, void *))
+{
+	switch (direction) {
+		case USB_DIRECTION_IN:
+			if (!transfer_in_func) {
+				ipc_answer_0(callid, ENOTSUP);
+				return;
+			}
+			break;
+		case USB_DIRECTION_OUT:
+			if (!transfer_out_func) {
+				ipc_answer_0(callid, ENOTSUP);
+				return;
+			}
+			break;
+		default:
+			assert(false && "unreachable code");
+			break;
+	}
+
 	usb_target_t target = {
 		.address = IPC_GET_ARG1(*call),
@@ -244,22 +288,4 @@
 	};
 
-	size_t len = 0;
-	void *buffer = NULL;
-	if (expected_len > 0) {
-		int rc = async_data_write_accept(&buffer, false,
-		    1, USB_MAX_PAYLOAD_SIZE,
-		    0, &len);
-
-		if (rc != EOK) {
-			ipc_answer_0(callid, rc);
-			return;
-		}
-	}
-
-	if (!usb_iface->control_write_setup) {
-		ipc_answer_0(callid, ENOTSUP);
-		return;
-	}
-
 	async_transaction_t *trans = malloc(sizeof(async_transaction_t));
 	trans->caller = callid;
@@ -267,6 +293,18 @@
 	trans->size = 0;
 
-	int rc = usb_iface->control_write_setup(device, target, buffer, len,
-	    callback_out, trans);
+	int rc;
+	switch (direction) {
+		case USB_DIRECTION_IN:
+			rc = transfer_in_func(device, target,
+			    callback_in, trans);
+			break;
+		case USB_DIRECTION_OUT:
+			rc = transfer_out_func(device, target,
+			    callback_out, trans);
+			break;
+		default:
+			assert(false && "unreachable code");
+			break;
+	}
 
 	if (rc != EOK) {
@@ -274,119 +312,66 @@
 		free(trans);
 	}
+	return;
+}
+
+
+void remote_usbhc_interrupt_out(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);
+
+	return remote_usbhc_out_transfer(device, callid, call,
+	    usb_iface->interrupt_out);
+}
+
+void remote_usbhc_interrupt_in(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);
+
+	return remote_usbhc_in_transfer(device, callid, call,
+	    usb_iface->interrupt_in);
+}
+
+void remote_usbhc_control_write_setup(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);
+
+	return remote_usbhc_out_transfer(device, callid, call,
+	    usb_iface->control_write_setup);
 }
 
 void remote_usbhc_control_write_data(device_t *device, void *iface,
-	    ipc_callid_t callid, ipc_call_t *call)
-{
-	usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
-
-	size_t expected_len = IPC_GET_ARG3(*call);
-	usb_target_t target = {
-		.address = IPC_GET_ARG1(*call),
-		.endpoint = IPC_GET_ARG2(*call)
-	};
-
-	size_t len = 0;
-	void *buffer = NULL;
-	if (expected_len > 0) {
-		int rc = async_data_write_accept(&buffer, false,
-		    1, USB_MAX_PAYLOAD_SIZE,
-		    0, &len);
-
-		if (rc != EOK) {
-			ipc_answer_0(callid, rc);
-			return;
-		}
-	}
-
-	if (!usb_iface->control_write_data) {
-		ipc_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	async_transaction_t *trans = malloc(sizeof(async_transaction_t));
-	trans->caller = callid;
-	trans->buffer = NULL;
-	trans->size = 0;
-
-	int rc = usb_iface->control_write_data(device, target, buffer, len,
-	    callback_out, trans);
-
-	if (rc != EOK) {
-		ipc_answer_0(callid, rc);
-		free(trans);
-	}
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
+	assert(usb_iface != NULL);
+
+	return remote_usbhc_out_transfer(device, callid, call,
+	    usb_iface->control_write_data);
 }
 
 void remote_usbhc_control_write_status(device_t *device, void *iface,
-	    ipc_callid_t callid, ipc_call_t *call)
-{
-	usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
-
-	usb_target_t target = {
-		.address = IPC_GET_ARG1(*call),
-		.endpoint = IPC_GET_ARG2(*call)
-	};
-
-	if (!usb_iface->control_write_status) {
-		ipc_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	async_transaction_t *trans = malloc(sizeof(async_transaction_t));
-	trans->caller = callid;
-	trans->buffer = NULL;
-	trans->size = 0;
-
-	int rc = usb_iface->control_write_status(device, target,
-	    callback_in, trans);
-
-	if (rc != EOK) {
-		ipc_answer_0(callid, rc);
-		free(trans);
-	}
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
+	assert(usb_iface != NULL);
+
+	return remote_usbhc_status_transfer(device, callid, call,
+	    USB_DIRECTION_IN, usb_iface->control_write_status, NULL);
 }
 
 void remote_usbhc_control_read_setup(device_t *device, void *iface,
-	    ipc_callid_t callid, ipc_call_t *call)
-{
-	usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
-
-	size_t expected_len = IPC_GET_ARG3(*call);
-	usb_target_t target = {
-		.address = IPC_GET_ARG1(*call),
-		.endpoint = IPC_GET_ARG2(*call)
-	};
-
-	size_t len = 0;
-	void *buffer = NULL;
-	if (expected_len > 0) {
-		int rc = async_data_write_accept(&buffer, false,
-		    1, USB_MAX_PAYLOAD_SIZE,
-		    0, &len);
-
-		if (rc != EOK) {
-			ipc_answer_0(callid, rc);
-			return;
-		}
-	}
-
-	if (!usb_iface->control_read_setup) {
-		ipc_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	async_transaction_t *trans = malloc(sizeof(async_transaction_t));
-	trans->caller = callid;
-	trans->buffer = NULL;
-	trans->size = 0;
-
-	int rc = usb_iface->control_read_setup(device, target, buffer, len,
-	    callback_out, trans);
-
-	if (rc != EOK) {
-		ipc_answer_0(callid, rc);
-		free(trans);
-	}
+    ipc_callid_t callid, ipc_call_t *call)
+{
+	usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
+	assert(usb_iface != NULL);
+
+	return remote_usbhc_out_transfer(device, callid, call,
+	    usb_iface->control_read_setup);
 }
 
@@ -395,29 +380,8 @@
 {
 	usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
-
-	size_t len = IPC_GET_ARG3(*call);
-	usb_target_t target = {
-		.address = IPC_GET_ARG1(*call),
-		.endpoint = IPC_GET_ARG2(*call)
-	};
-
-	if (!usb_iface->control_read_data) {
-		ipc_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	async_transaction_t *trans = malloc(sizeof(async_transaction_t));
-	trans->caller = callid;
-	trans->buffer = malloc(len);
-	trans->size = len;
-
-	int rc = usb_iface->control_read_data(device, target, trans->buffer, len,
-	    callback_in, trans);
-
-	if (rc != EOK) {
-		ipc_answer_0(callid, rc);
-		free(trans->buffer);
-		free(trans);
-	}
+	assert(usb_iface != NULL);
+
+	return remote_usbhc_in_transfer(device, callid, call,
+	    usb_iface->control_read_data);
 }
 
@@ -426,27 +390,8 @@
 {
 	usbhc_iface_t *usb_iface = (usbhc_iface_t *) iface;
-
-	usb_target_t target = {
-		.address = IPC_GET_ARG1(*call),
-		.endpoint = IPC_GET_ARG2(*call)
-	};
-
-	if (!usb_iface->control_read_status) {
-		ipc_answer_0(callid, ENOTSUP);
-		return;
-	}
-
-	async_transaction_t *trans = malloc(sizeof(async_transaction_t));
-	trans->caller = callid;
-	trans->buffer = NULL;
-	trans->size = 0;
-
-	int rc = usb_iface->control_read_status(device, target,
-	    callback_out, trans);
-
-	if (rc != EOK) {
-		ipc_answer_0(callid, rc);
-		free(trans);
-	}
+	assert(usb_iface != NULL);
+
+	return remote_usbhc_status_transfer(device, callid, call,
+	    USB_DIRECTION_OUT, NULL, usb_iface->control_read_status);
 }
 
