Index: uspace/drv/uhci-hcd/hc.c
===================================================================
--- uspace/drv/uhci-hcd/hc.c	(revision 89864128256491f600079ac9523dd30bc0833056)
+++ uspace/drv/uhci-hcd/hc.c	(revision 4c28d176409a75c11df92e60041565d595505d04)
@@ -44,10 +44,20 @@
 #include "hc.h"
 
-#define UHCI_INTR_ALLOW_INTERRUPTS \
-    (UHCI_INTR_CRC | UHCI_INTR_COMPLETE | UHCI_INTR_SHORT_PACKET)
-#define UHCI_STATUS_USED_INTERRUPTS \
-    (UHCI_STATUS_INTERRUPT | UHCI_STATUS_ERROR_INTERRUPT)
-
-
+static irq_cmd_t uhci_cmds[] = {
+	{
+		.cmd = CMD_PIO_READ_16,
+		.addr = NULL, /* patched for every instance */
+		.dstarg = 1
+	},
+	{
+		.cmd = CMD_PIO_WRITE_16,
+		.addr = NULL, /* pathed for every instance */
+		.value = 0x1f
+	},
+	{
+		.cmd = CMD_ACCEPT
+	}
+};
+/*----------------------------------------------------------------------------*/
 static int hc_init_transfer_lists(hc_t *instance);
 static int hc_init_mem_structures(hc_t *instance);
@@ -56,4 +66,8 @@
 static int hc_interrupt_emulator(void *arg);
 static int hc_debug_checker(void *arg);
+#if 0
+static bool usb_is_allowed(
+    bool low_speed, usb_transfer_type_t transfer, size_t size);
+#endif
 /*----------------------------------------------------------------------------*/
 /** Initialize UHCI hcd driver structure
@@ -75,7 +89,9 @@
 	int ret;
 
-#define CHECK_RET_RETURN(ret, message...) \
+#define CHECK_RET_DEST_FUN_RETURN(ret, message...) \
 	if (ret != EOK) { \
 		usb_log_error(message); \
+		if (instance->ddf_instance) \
+			ddf_fun_destroy(instance->ddf_instance); \
 		return ret; \
 	} else (void) 0
@@ -83,27 +99,34 @@
 	instance->hw_interrupts = interrupts;
 	instance->hw_failures = 0;
+
+	/* Setup UHCI function. */
+	instance->ddf_instance = fun;
 
 	/* allow access to hc control registers */
 	regs_t *io;
 	ret = pio_enable(regs, reg_size, (void**)&io);
-	CHECK_RET_RETURN(ret,
+	CHECK_RET_DEST_FUN_RETURN(ret,
 	    "Failed(%d) to gain access to registers at %p: %s.\n",
-	    ret, io, str_error(ret));
+	    ret, str_error(ret), io);
 	instance->registers = io;
-	usb_log_debug("Device registers at %p (%zuB) accessible.\n",
+	usb_log_debug("Device registers at %p(%u) accessible.\n",
 	    io, reg_size);
 
 	ret = hc_init_mem_structures(instance);
-	CHECK_RET_RETURN(ret,
-	    "Failed(%d) to initialize UHCI memory structures: %s.\n",
-	    ret, str_error(ret));
+	CHECK_RET_DEST_FUN_RETURN(ret,
+	    "Failed to initialize UHCI memory structures.\n");
 
 	hc_init_hw(instance);
 	if (!interrupts) {
-		instance->interrupt_emulator =
+		instance->cleaner =
 		    fibril_create(hc_interrupt_emulator, instance);
-		fibril_add_ready(instance->interrupt_emulator);
-	}
-	(void)hc_debug_checker;
+		fibril_add_ready(instance->cleaner);
+	} else {
+		/* TODO: enable interrupts here */
+	}
+
+	instance->debug_checker =
+	    fibril_create(hc_debug_checker, instance);
+//	fibril_add_ready(instance->debug_checker);
 
 	return EOK;
@@ -141,5 +164,5 @@
 		/* Enable all interrupts, but resume interrupt */
 		pio_write_16(&instance->registers->usbintr,
-		    UHCI_INTR_ALLOW_INTERRUPTS);
+		    UHCI_INTR_CRC | UHCI_INTR_COMPLETE | UHCI_INTR_SHORT_PACKET);
 	}
 
@@ -167,46 +190,32 @@
 {
 	assert(instance);
-#define CHECK_RET_RETURN(ret, message...) \
+#define CHECK_RET_DEST_CMDS_RETURN(ret, message...) \
 	if (ret != EOK) { \
 		usb_log_error(message); \
+		if (instance->interrupt_code.cmds != NULL) \
+			free(instance->interrupt_code.cmds); \
 		return ret; \
 	} else (void) 0
 
 	/* Init interrupt code */
-	instance->interrupt_code.cmds = instance->interrupt_commands;
+	instance->interrupt_code.cmds = malloc(sizeof(uhci_cmds));
+	int ret = (instance->interrupt_code.cmds == NULL) ? ENOMEM : EOK;
+	CHECK_RET_DEST_CMDS_RETURN(ret,
+	    "Failed to allocate interrupt cmds space.\n");
+
 	{
-		/* Read status register */
-		instance->interrupt_commands[0].cmd = CMD_PIO_READ_16;
-		instance->interrupt_commands[0].dstarg = 1;
-		instance->interrupt_commands[0].addr =
-		    &instance->registers->usbsts;
-
-		/* Test whether we are the interrupt cause */
-		instance->interrupt_commands[1].cmd = CMD_BTEST;
-		instance->interrupt_commands[1].value =
-		    UHCI_STATUS_USED_INTERRUPTS | UHCI_STATUS_NM_INTERRUPTS;
-		instance->interrupt_commands[1].srcarg = 1;
-		instance->interrupt_commands[1].dstarg = 2;
-
-		/* Predicate cleaning and accepting */
-		instance->interrupt_commands[2].cmd = CMD_PREDICATE;
-		instance->interrupt_commands[2].value = 2;
-		instance->interrupt_commands[2].srcarg = 2;
-
-		/* Write clean status register */
-		instance->interrupt_commands[3].cmd = CMD_PIO_WRITE_A_16;
-		instance->interrupt_commands[3].srcarg = 1;
-		instance->interrupt_commands[3].addr =
-		    &instance->registers->usbsts;
-
-		/* Accept interrupt */
-		instance->interrupt_commands[4].cmd = CMD_ACCEPT;
-
-		instance->interrupt_code.cmdcount = UHCI_NEEDED_IRQ_COMMANDS;
+		irq_cmd_t *interrupt_commands = instance->interrupt_code.cmds;
+		memcpy(interrupt_commands, uhci_cmds, sizeof(uhci_cmds));
+		interrupt_commands[0].addr =
+		    (void*)&instance->registers->usbsts;
+		interrupt_commands[1].addr =
+		    (void*)&instance->registers->usbsts;
+		instance->interrupt_code.cmdcount =
+		    sizeof(uhci_cmds) / sizeof(irq_cmd_t);
 	}
 
 	/* Init transfer lists */
-	int ret = hc_init_transfer_lists(instance);
-	CHECK_RET_RETURN(ret, "Failed to init transfer lists.\n");
+	ret = hc_init_transfer_lists(instance);
+	CHECK_RET_DEST_CMDS_RETURN(ret, "Failed to init transfer lists.\n");
 	usb_log_debug("Initialized transfer lists.\n");
 
@@ -214,10 +223,11 @@
 	instance->frame_list = get_page();
 	ret = instance ? EOK : ENOMEM;
-	CHECK_RET_RETURN(ret, "Failed to get frame list page.\n");
+	CHECK_RET_DEST_CMDS_RETURN(ret, "Failed to get frame list page.\n");
 	usb_log_debug("Initialized frame list at %p.\n", instance->frame_list);
 
 	/* Set all frames to point to the first queue head */
-	const uint32_t queue = LINK_POINTER_QH(
-	        addr_to_phys(instance->transfers_interrupt.queue_head));
+	const uint32_t queue =
+	  instance->transfers_interrupt.queue_head_pa
+	  | LINK_POINTER_QUEUE_HEAD_FLAG;
 
 	unsigned i = 0;
@@ -226,5 +236,5 @@
 	}
 
-	/* Init device keeper */
+	/* Init device keeper*/
 	usb_device_keeper_init(&instance->manager);
 	usb_log_debug("Initialized device manager.\n");
@@ -232,9 +242,8 @@
 	ret = usb_endpoint_manager_init(&instance->ep_manager,
 	    BANDWIDTH_AVAILABLE_USB11);
-	CHECK_RET_RETURN(ret, "Failed to initialize endpoint manager: %s.\n",
-	    str_error(ret));
+	assert(ret == EOK);
 
 	return EOK;
-#undef CHECK_RET_RETURN
+#undef CHECK_RET_DEST_CMDS_RETURN
 }
 /*----------------------------------------------------------------------------*/
@@ -251,10 +260,7 @@
 {
 	assert(instance);
-#define SETUP_TRANSFER_LIST(type, name) \
-do { \
-	int ret = transfer_list_init(&instance->transfers_##type, name); \
+#define CHECK_RET_CLEAR_RETURN(ret, message...) \
 	if (ret != EOK) { \
-		usb_log_error("Failed(%d) to setup %s transfer list: %s.\n", \
-		    ret, name, str_error(ret)); \
+		usb_log_error(message); \
 		transfer_list_fini(&instance->transfers_bulk_full); \
 		transfer_list_fini(&instance->transfers_control_full); \
@@ -262,13 +268,22 @@
 		transfer_list_fini(&instance->transfers_interrupt); \
 		return ret; \
-	} \
-} while (0)
-
-	SETUP_TRANSFER_LIST(bulk_full, "BULK FULL");
-	SETUP_TRANSFER_LIST(control_full, "CONTROL FULL");
-	SETUP_TRANSFER_LIST(control_slow, "CONTROL LOW");
-	SETUP_TRANSFER_LIST(interrupt, "INTERRUPT");
-#undef SETUP_TRANSFER_LIST
-	/* Connect lists into one schedule */
+	} else (void) 0
+
+	/* initialize TODO: check errors */
+	int ret;
+	ret = transfer_list_init(&instance->transfers_bulk_full, "BULK_FULL");
+	CHECK_RET_CLEAR_RETURN(ret, "Failed to init BULK list.");
+
+	ret = transfer_list_init(
+	    &instance->transfers_control_full, "CONTROL_FULL");
+	CHECK_RET_CLEAR_RETURN(ret, "Failed to init CONTROL FULL list.");
+
+	ret = transfer_list_init(
+	    &instance->transfers_control_slow, "CONTROL_SLOW");
+	CHECK_RET_CLEAR_RETURN(ret, "Failed to init CONTROL SLOW list.");
+
+	ret = transfer_list_init(&instance->transfers_interrupt, "INTERRUPT");
+	CHECK_RET_CLEAR_RETURN(ret, "Failed to init INTERRUPT list.");
+
 	transfer_list_set_next(&instance->transfers_control_full,
 		&instance->transfers_bulk_full);
@@ -281,5 +296,5 @@
 #ifdef FSBR
 	transfer_list_set_next(&instance->transfers_bulk_full,
-	    &instance->transfers_control_full);
+		&instance->transfers_control_full);
 #endif
 
@@ -314,5 +329,5 @@
 
 	transfer_list_t *list =
-	    instance->transfers[batch->ep->speed][batch->ep->transfer_type];
+	    instance->transfers[batch->speed][batch->transfer_type];
 	assert(list);
 	transfer_list_add_batch(list, batch);
@@ -334,6 +349,8 @@
 {
 	assert(instance);
+//	status |= 1; //Uncomment to work around qemu hang
+	/* TODO: Resume interrupts are not supported */
 	/* Lower 2 bits are transaction error and transaction complete */
-	if (status & (UHCI_STATUS_INTERRUPT | UHCI_STATUS_ERROR_INTERRUPT)) {
+	if (status & 0x3) {
 		LIST_INITIALIZE(done);
 		transfer_list_remove_finished(
@@ -354,11 +371,6 @@
 		}
 	}
-	/* Resume interrupts are not supported */
-	if (status & UHCI_STATUS_RESUME) {
-		usb_log_error("Resume interrupt!\n");
-	}
-
-	/* Bits 4 and 5 indicate hc error */
-	if (status & (UHCI_STATUS_PROCESS_ERROR | UHCI_STATUS_SYSTEM_ERROR)) {
+	/* bits 4 and 5 indicate hc error */
+	if (status & 0x18) {
 		usb_log_error("UHCI hardware failure!.\n");
 		++instance->hw_failures;
@@ -386,19 +398,15 @@
 {
 	usb_log_debug("Started interrupt emulator.\n");
-	hc_t *instance = arg;
+	hc_t *instance = (hc_t*)arg;
 	assert(instance);
 
 	while (1) {
-		/* Read and clear status register */
+		/* read and ack interrupts */
 		uint16_t status = pio_read_16(&instance->registers->usbsts);
-		pio_write_16(&instance->registers->usbsts, status);
+		pio_write_16(&instance->registers->usbsts, 0x1f);
 		if (status != 0)
 			usb_log_debug2("UHCI status: %x.\n", status);
-// Qemu fails to report stalled communication
-// see https://bugs.launchpad.net/qemu/+bug/757654
-// This is a simple workaround to force queue processing every time
-	//	status |= 1;
 		hc_interrupt(instance, status);
-		async_usleep(UHCI_INT_EMULATOR_TIMEOUT);
+		async_usleep(UHCI_CLEANER_TIMEOUT);
 	}
 	return EOK;
@@ -412,5 +420,5 @@
 int hc_debug_checker(void *arg)
 {
-	hc_t *instance = arg;
+	hc_t *instance = (hc_t*)arg;
 	assert(instance);
 
@@ -433,6 +441,5 @@
 		if (frame_list != addr_to_phys(instance->frame_list)) {
 			usb_log_debug("Framelist address: %p vs. %p.\n",
-			    (void *) frame_list,
-			    (void *) addr_to_phys(instance->frame_list));
+			    frame_list, addr_to_phys(instance->frame_list));
 		}
 
@@ -443,6 +450,6 @@
 		uintptr_t real_pa = addr_to_phys(QH(interrupt));
 		if (expected_pa != real_pa) {
-			usb_log_debug("Interrupt QH: %p (frame %d) vs. %p.\n",
-			    (void *) expected_pa, frnum, (void *) real_pa);
+			usb_log_debug("Interrupt QH: %p(frame: %d) vs. %p.\n",
+			    expected_pa, frnum, real_pa);
 		}
 
@@ -451,5 +458,5 @@
 		if (expected_pa != real_pa) {
 			usb_log_debug("Control Slow QH: %p vs. %p.\n",
-			    (void *) expected_pa, (void *) real_pa);
+			    expected_pa, real_pa);
 		}
 
@@ -458,5 +465,5 @@
 		if (expected_pa != real_pa) {
 			usb_log_debug("Control Full QH: %p vs. %p.\n",
-			    (void *) expected_pa, (void *) real_pa);
+			    expected_pa, real_pa);
 		}
 
@@ -465,5 +472,5 @@
 		if (expected_pa != real_pa ) {
 			usb_log_debug("Bulk QH: %p vs. %p.\n",
-			    (void *) expected_pa, (void *) real_pa);
+			    expected_pa, real_pa);
 		}
 		async_usleep(UHCI_DEBUGER_TIMEOUT);
@@ -472,4 +479,31 @@
 #undef QH
 }
+/*----------------------------------------------------------------------------*/
+/** Check transfers for USB validity
+ *
+ * @param[in] low_speed Transfer speed.
+ * @param[in] transfer Transer type
+ * @param[in] size Size of data packets
+ * @return True if transaction is allowed by USB specs, false otherwise
+ */
+#if 0
+bool usb_is_allowed(
+    bool low_speed, usb_transfer_type_t transfer, size_t size)
+{
+	/* see USB specification chapter 5.5-5.8 for magic numbers used here */
+	switch(transfer)
+	{
+	case USB_TRANSFER_ISOCHRONOUS:
+		return (!low_speed && size < 1024);
+	case USB_TRANSFER_INTERRUPT:
+		return size <= (low_speed ? 8 : 64);
+	case USB_TRANSFER_CONTROL: /* device specifies its own max size */
+		return (size <= (low_speed ? 8 : 64));
+	case USB_TRANSFER_BULK: /* device specifies its own max size */
+		return (!low_speed && size <= 64);
+	}
+	return false;
+}
+#endif
 /**
  * @}
