* [Qemu-devel] [kvm-devel] [PATCH] USB 2.0 EHCI emulation
@ 2008-01-30 10:03 ` Arnon Gilboa
2008-02-28 19:46 ` [kvm-devel] [Qemu-devel] " Gerb Stralko
0 siblings, 1 reply; 5+ messages in thread
From: Arnon Gilboa @ 2008-01-30 10:03 UTC (permalink / raw)
To: qemu-devel, kvm-devel
[-- Attachment #1: Type: text/plain, Size: 2136 bytes --]
Hi,
Attached is a repost of the preliminary patch implementing USB 2.0 EHCI
emulation.
http://lists.gnu.org/archive/html/qemu-devel/2008-01/msg00144.html
Paul's comments regarding the timing resolution and accuracy problems is
more
than right. It is relevant for any device emulation which requires
hi-res timers.
However, from my experince, guests like WinXP can easily handle these
timing issues. Use this patch and try it by yourself. I believe that
with a few
more bits of code in usb-ehci.c, Linux and other guests will work with
the ehci
emulation as well.
The current status is as following:
-The patch was tested on WinXP guest with both qemu-head & kvm.
-It was tried on FC7 guest as well, but in this case there are still
some control transfers problems.
-Tested on USB 2.0 DoK, USB 1.1 multi-function DoK, USB 2.0 HP DeskJet
multi-function printer/scanner.
-Performance is almost similar to UHCI emulation. There are still many
possible performance improvements.
-Asynchronous schedule is supported, which means Bulk and Control
transfers are working.
-Periodic schedule is currently NOT supported, so Interrupt and
Isochronous transfers are NOT working (no webcam or audio devices
support).
-Currently async transfers are serialized for simplicity, so at each
moment only a single pending transfer is allowed. This is similar to the
UHCI & OHCI emulations.
-The host kernel was configured with dynamic tick & hi-res timers, to
allow the desired timer resolution. USB 2.0 microframe is 125usec.
-In usb-linux.c, usb_host_handle_data() was rewritten for EHCI with the
option of async i/o. See the defines USE_ASYNCIO & USE_EHCI.
-There are possible end-of-micro-frame drifts when
usb_host_handle_control()'s synchronous ioctls take too long.
TODOs:
-Support periodic schedule
-Support linux guest
-Parallelize async transfers - support several pending QHs
-Implement async control transfers, prevent microframe drifts
-Move EHCIState state machine members into ehci_qh_state_machine()
(async_qh_addr; async_qh; usb_packet; usb_buf)
Waiting for your comments,
Arnon
[-- Attachment #2: usb-ehci.patch --]
[-- Type: application/octet-stream, Size: 52159 bytes --]
diff -u -d -p -P -r qemu-head/hw/pci.h qemu-merge/hw/pci.h
--- qemu-head/hw/pci.h 2008-01-07 15:27:02.000000000 +0200
+++ qemu-merge/hw/pci.h 2008-01-07 14:54:51.000009000 +0200
@@ -112,6 +112,9 @@ void usb_uhci_piix4_init(PCIBus *bus, in
/* usb-ohci.c */
void usb_ohci_init_pci(struct PCIBus *bus, int num_ports, int devfn);
+/* usb-ehci.c */
+void usb_ehci_init(PCIBus *bus, int devfn);
+
/* eepro100.c */
void pci_i82551_init(PCIBus *bus, NICInfo *nd, int devfn);
diff -u -d -p -P -r qemu-head/hw/usb-ehci.c qemu-merge/hw/usb-ehci.c
--- qemu-head/hw/usb-ehci.c 1970-01-01 02:00:00.000000000 +0200
+++ qemu-merge/hw/usb-ehci.c 2008-01-07 18:19:47.050569214 +0200
@@ -0,0 +1,1339 @@
+/*
+ * USB EHCI controller emulation
+ *
+ * Copyright (C) 2007 Qumranet
+ *
+ * Author:
+ *
+ * Arnon Gilboa <arnong@qumranet.com>
+ *
+ * This work is licensed under the GNU LGPL license, version 2.
+ */
+#include <stddef.h>
+#include "hw.h"
+#include "usb.h"
+#include "pci.h"
+#include "qemu-timer.h"
+
+//#define DEBUG_EHCI
+//#define DEBUG_PACKET
+
+#ifdef DEBUG_EHCI
+#define dprintf printf
+#else
+#define dprintf(...)
+#endif
+
+/* microframes (125usec) in second */
+#define MICROFRAME_TIMER_FREQ 8000
+#define ASYNC_SLEEP_TIMER_FREQ 100000
+
+#define NB_PORTS 4
+
+#define PAGE_SIZE 4096
+#define MAX_TX_SIZE 4*PAGE_SIZE
+
+typedef struct ehci_qh {
+ uint32_t next;
+ uint32_t info1;
+ uint32_t info2;
+ uint32_t current;
+ uint32_t qtd_next;
+ uint32_t alt_next;
+ uint32_t token;
+ uint32_t buf[5];
+} ehci_qh;
+
+typedef struct ehci_qtd {
+ uint32_t next;
+ uint32_t alt_next;
+ uint32_t token;
+ uint32_t buf[5];
+} ehci_qtd;
+
+typedef struct ehci_itd {
+ uint32_t next;
+ uint32_t transaction[8];
+ uint32_t bufp[7];
+} ehci_itd;
+
+typedef struct ehci_sitd {
+ uint32_t next;
+ uint32_t fullspeed_ep;
+ uint32_t uframe;
+ uint32_t results;
+ uint32_t buf[2];
+ uint32_t backpointer;
+} ehci_sitd;
+
+typedef struct ehci_fstn {
+ uint32_t next;
+ uint32_t prev;
+} ehci_fstn;
+
+typedef struct EHCIPort {
+ USBPort port;
+ uint32_t ctrl;
+} EHCIPort;
+
+/* FIXME: support variable num_ports, given in usb_ehci_init() */
+typedef struct EHCIState {
+ PCIDevice dev;
+ target_phys_addr_t mem_base;
+ int mem;
+
+ uint32_t command;
+ uint32_t status;
+ uint32_t intr_enable;
+ uint32_t frame_index;
+ uint32_t segment;
+ uint32_t frame_list;
+ uint32_t async_next;
+ uint32_t configured_flag;
+
+ EHCIPort ports[NB_PORTS];
+ QEMUTimer *frame_timer;
+ QEMUTimer *async_timer;
+
+ int async_state;
+ int nak_state;
+
+ uint32_t async_qh_addr;
+ ehci_qh async_qh;
+ USBPacket usb_packet;
+ int pending_qh;
+ uint8_t usb_buf[MAX_TX_SIZE];
+} EHCIState;
+
+static const uint8_t ehci_pci_config[] = {
+ 0x31, 0x11, /* Vendor ID */
+ 0x62, 0x15, /* Device ID */
+ 0x00, 0x00, /* Command */
+ 0x00, 0x02, /* Status - Capabilities Disabled */
+ 0x30, /* REVID */
+ 0x20, 0x03, 0x0c, /* Class Code - USB 2.0 Host Controller */
+ 0x00, /* CacheLine Size */
+ 0x00, /* Latency Timer */
+ 0x80, /* Header Type - Multi-Function Device */
+ 0x00, /* BIST */
+ 0x00, 0x00, 0x00, 0x00, /* BAR0 */
+ 0x00, 0x00, 0x00, 0x00, /* BAR1 */
+ 0x00, 0x00, 0x00, 0x00, /* BAR2 */
+ 0x00, 0x00, 0x00, 0x00, /* BAR3 */
+ 0x00, 0x00, 0x00, 0x00, /* BAR4 */
+ 0x00, 0x00, 0x00, 0x00, /* BAR5 */
+ 0x00, 0x00, 0x00, 0x00, /* Cardbus CIS Pointer */
+ 0x31, 0x11, /* Subsystem Vendor ID */
+ 0x62, 0x15, /* Subsystem Device ID */
+ 0x00, 0x00, 0x00, 0x00, /* Expansion ROM Base Address */
+ 0x00, /* Capabilities Pointer - Disabled */
+ 0x00, 0x00, 0x00, /* Reserved */
+ 0x00, 0x00, 0x00, 0x00, /* Reserved */
+ 0x00, /* Interrupt Line */
+ 0x01, /* Interrupt Pin - INTA# */
+ 0x02, /* Minimum Grant */
+ 0x10, /* Maximum Latency */
+ 0x00, /* TRDY Timeout */
+ 0x80, /* Retry Timeout */
+ 0x00, 0x00 /* Reserved */
+};
+
+static const uint8_t ehci_specific_pci_config[] = {
+ 0x20, /* Serial Bus Spec - Release 2.0 */
+ 0x20, /* Frame Length Adjustment Register */
+ 0x1f, 0x00 /* Port Wake Capability Register - 4 Ports */
+};
+
+static const uint8_t ehci_power_mgmt_config[] = {
+ 0x01, /* Capability Identifier */
+ 0x00, /* Next Item Pointer */
+ 0x02, 0xff, /* Power Management Capabilities */
+ 0x00, 0x1f, /* Power Management Control/Status */
+ 0x00, /* Bridge Support Extensions */
+ 0x00 /* Data */
+};
+
+#define EHCI_CMD_RS (1 << 0)
+#define EHCI_CMD_HCRESET (1 << 1)
+#define EHCI_CMD_FLS_SHIFT 2
+#define EHCI_CMD_FLS_MASK (3 << EHCI_CMD_FLS_SHIFT)
+#define EHCI_CMD_PSE (1 << 4)
+#define EHCI_CMD_ASE (1 << 5)
+#define EHCI_CMD_IAAD (1 << 6)
+#define EHCI_CMD_LHCR (1 << 7)
+#define EHCI_CMD_ITC_SHIFT 16
+#define EHCI_CMD_ITC_MASK (0xff << EHCI_CMD_ITC_SHIFT)
+
+#define EHCI_STS_USBINT (1 << 0)
+#define EHCI_STS_USBERRINT (1 << 1)
+#define EHCI_STS_PCD (1 << 2)
+#define EHCI_STS_FLR (1 << 3)
+#define EHCI_STS_HSE (1 << 4)
+#define EHCI_STS_IAA (1 << 5)
+#define EHCI_STS_HCH (1 << 12)
+#define EHCI_STS_RECL (1 << 13)
+#define EHCI_STS_PSSTAT (1 << 14)
+#define EHCI_STS_ASS (1 << 15)
+
+#define EHCI_INTR_USBINTE (1 << 0)
+#define EHCI_INTR_USBERRINTE (1 << 1)
+#define EHCI_INTR_PCIE (1 << 2)
+#define EHCI_INTR_FLRE (1 << 3)
+#define EHCI_INTR_HSEE (1 << 4)
+#define EHCI_INTR_IAAE (1 << 5)
+
+#define EHCI_PRT_ECCS (1 << 0)
+#define EHCI_PRT_ECSC (1 << 1)
+#define EHCI_PRT_PED (1 << 2)
+#define EHCI_PRT_PEDC (1 << 3)
+#define EHCI_PRT_OCA (1 << 4)
+#define EHCI_PRT_OCC (1 << 5)
+#define EHCI_PRT_FPR (1 << 6)
+#define EHCI_PRT_SUSP (1 << 7)
+#define EHCI_PRT_PR (1 << 8)
+#define EHCI_PRT_LS_SHIFT 10
+#define EHCI_PRT_LS_MASK (3 << EHCI_PRT_LS_SHIFT)
+#define EHCI_PRT_PP (1 << 12)
+#define EHCI_PRT_PO (1 << 13)
+#define EHCI_PRT_PIC_SHIFT 14
+#define EHCI_PRT_PIC_MASK (3 << EHCI_PRT_PIC_SHIFT)
+#define EHCI_PRT_PTC_SHIFT 16
+#define EHCI_PRT_PTC_MASK (0xf << EHCI_PRT_PTC_SHIFT)
+#define EHCI_PRT_WKCNNT_E (1 << 20)
+#define EHCI_PRT_WKDSCNNT_E (1 << 21)
+#define EHCI_PRT_WKOC_E (1 << 22)
+
+#define EHCI_COMMAND_MASK 0x00ff007f
+#define EHCI_STATUS_MASK 0x0000003f
+#define EHCI_INTR_MASK 0x0000003f
+#define EHCI_FRAME_INDEX_MASK 0x00003fff
+#define EHCI_FRAME_LIST_MASK 0xfffff000
+#define EHCI_ASYNC_NEXT_MASK 0xffffffe0
+#define EHCI_CONFIGURED_FLAG_MASK 0x00000001
+#define EHCI_PORT_MASK 0x00eff1c4
+#define EHCI_PORT_WC_MASK 0x0000002a
+
+/* Data Structures and Bitmasks - EHCI spec ch3 */
+#define EHCI_LINK_T 1
+#define EHCI_LINK_TYP_SHIFT 1
+#define EHCI_LINK_TYP_MASK (3 << EHCI_LINK_TYP_SHIFT)
+#define EHCI_LINK_Z 0x18
+#define EHCI_LINK_PTR 0xffffffe0
+
+#define EHCI_LINK_TYP_ITD 0
+#define EHCI_LINK_TYP_QH 1
+#define EHCI_LINK_TYP_SITD 2
+#define EHCI_LINK_TYP_FSTN 3
+
+/* FIXME: J/K-state values are inconsistent between EHCI specs */
+#define EHCI_PORT_LS_SE0 0
+#define EHCI_PORT_LS_KSTATE 1
+#define EHCI_PORT_LS_JSTATE 2
+
+/* QH bitmasks */
+#define EHCI_QH_DEV_ADDR 0x7f
+#define EHCI_QH_I (1 << 7)
+#define EHCI_QH_ENDPT_SHIFT 8
+#define EHCI_QH_ENDPT_MASK (0xf << EHCI_QH_ENDPT_SHIFT)
+#define EHCI_QH_EPS_SHIFT 12
+#define EHCI_QH_EPS_MASK (3 << EHCI_QH_EPS_SHIFT)
+#define EHCI_QH_DTC (1 << 14)
+#define EHCI_QH_H (1 << 15)
+#define EHCI_QH_MAX_PKT_LEN_SHIFT 16
+#define EHCI_QH_MAX_PKT_LEN_MASK (0x7ff << EHCI_QH_MAX_PKT_LEN_SHIFT)
+#define EHCI_QH_RL_SHIFT 28
+#define EHCI_QH_RL_MASK (0xf << EHCI_QH_RL_SHIFT)
+#define EHCI_QH_S_MASK 0xff
+
+#define EHCI_QH_EPS_FULL 0
+#define EHCI_QH_EPS_LOW 1
+#define EHCI_QH_EPS_HIGH 2
+#define EHCI_QH_EPS_RESERVED 3
+
+/* QH & QTD bitmasks */
+#define EHCI_QH_T 1
+#define EHCI_QH_NAK_CNT_SHIFT 1
+#define EHCI_QH_NAK_CNT_MASK (0xf << EHCI_QH_NAK_CNT_SHIFT)
+#define EHCI_QH_STATUS_SHIFT 0
+#define EHCI_QH_STATUS_MASK 0xff
+#define EHCI_QH_PID_CODE_SHIFT 8
+#define EHCI_QH_PID_CODE_MASK (3 << EHCI_QH_PID_CODE_SHIFT)
+#define EHCI_QH_CERR_SHIFT 10
+#define EHCI_QH_CERR_MASK (3 << EHCI_QH_CERR_SHIFT)
+#define EHCI_QH_C_PAGE_SHIFT 12
+#define EHCI_QH_C_PAGE_MASK (7 << EHCI_QH_C_PAGE_SHIFT)
+#define EHCI_QH_IOC (1 << 15)
+#define EHCI_QH_TOTAL_BYTES_SHIFT 16
+#define EHCI_QH_TOTAL_BYTES_MASK (0x7fff << EHCI_QH_TOTAL_BYTES_SHIFT)
+#define EHCI_QH_DT (1 << 31)
+#define EHCI_QH_CURR_OFFSET_SHIFT 0
+#define EHCI_QH_CURR_OFFSET_MASK (0xfff << EHCI_QH_CURR_OFFSET_SHIFT)
+#define EHCI_QH_BUFFER_POINTER 0xfffff000
+#define EHCI_QH_C_PROG_MASK 0xff
+#define EHCI_QH_FRAME_TAG 0x1f
+#define EHCI_QTD_WRITE_BACK \
+ (EHCI_QH_STATUS_MASK | EHCI_QH_CERR_MASK | EHCI_QH_TOTAL_BYTES_MASK)
+
+#define EHCI_QH_STS_PING (1 << 0)
+#define EHCI_QH_STS_STS (1 << 1)
+#define EHCI_QH_STS_MMF (1 << 2)
+#define EHCI_QH_STS_XACT (1 << 3)
+#define EHCI_QH_STS_BABBLE (1 << 4)
+#define EHCI_QH_STS_DBE (1 << 5)
+#define EHCI_QH_STS_HALT (1 << 6)
+#define EHCI_QH_STS_ACTIVE (1 << 7)
+
+#define ACTIVE_QH(qh) (qh->token & EHCI_QH_STS_ACTIVE)
+#define HALTED_QH(qh) (qh->token & EHCI_QH_STS_HALT)
+
+#define EHCI_QH_PID_OUT 0
+#define EHCI_QH_PID_IN 1
+#define EHCI_QH_PID_SETUP 2
+#define EHCI_QH_PID_RESERVED 3
+
+#define EHCI_BM(val, field) \
+ (((val) & EHCI_##field##_MASK) >> EHCI_##field##_SHIFT)
+
+#define EHCI_SET_BM(val, field, newval) do { \
+ val &= ~EHCI_##field##_MASK; \
+ val |= ((newval) << EHCI_##field##_SHIFT) & EHCI_##field##_MASK; \
+ } while(0)
+
+#define BIT(val, bitmask) (((val) & (bitmask)) ? 1 : 0)
+
+/* Async schedule states */
+#define EHCI_ASYNC_STATE_NOT_ACTIVE 1
+#define EHCI_ASYNC_STATE_ACTIVE 2
+#define EHCI_ASYNC_STATE_SLEEPING 3
+
+/* Nak counter reload states */
+#define EHCI_NAK_STATE_WAIT_FOR_HEAD 1
+#define EHCI_NAK_STATE_DO_RELOAD 2
+#define EHCI_NAK_STATE_WAIT_FOR_START 3
+
+/* Queue head traversal states */
+#define EHCI_QH_STATE_FETCH_QH 1
+#define EHCI_QH_STATE_ADVANCE_QUEUE 2
+#define EHCI_QH_STATE_EXEC_TRANSACTION 3
+#define EHCI_QH_STATE_WRITE_BACK_QTD 4
+#define EHCI_QH_STATE_FOLLOW_QHLP 5
+#define EHCI_QH_STATE_DONE 6
+
+static void ehci_update_irq(EHCIState *ehci)
+{
+ int level;
+
+ /* FIXME: check the STS & INTR bit-by bit? */
+ if (ehci->status & ehci->intr_enable & EHCI_INTR_MASK) {
+ level = 1;
+ } else {
+ level = 0;
+ }
+
+ qemu_set_irq(ehci->dev.irq[0], level);
+}
+
+static inline void ehci_set_irq(EHCIState *ehci, uint32_t intr)
+{
+ ehci->status |= intr;
+ ehci_update_irq(ehci);
+}
+
+static void ehci_attach(USBPort *p, USBDevice *dev)
+{
+ EHCIState *ehci = p->opaque;
+ EHCIPort *port = &ehci->ports[p->index];
+ uint32_t old_state = port->ctrl;
+
+ if (dev) {
+ if (port->port.dev) {
+ usb_attach(p, NULL);
+ }
+
+ /* set connect status */
+ if (!(port->ctrl & EHCI_PRT_ECCS)) {
+ port->ctrl |= (EHCI_PRT_ECCS | EHCI_PRT_ECSC);
+ }
+
+ /* FIXME: update speed */
+ dprintf("ehci_attach: dev_speed=%d\n", dev->speed);
+ if (dev->speed == USB_SPEED_LOW)
+ EHCI_SET_BM(port->ctrl, PRT_LS, EHCI_PORT_LS_KSTATE);
+ else
+ EHCI_SET_BM(port->ctrl, PRT_LS, EHCI_PORT_LS_JSTATE);
+
+ port->port.dev = dev;
+
+ /* FIXME: notify of remote-wakeup */
+
+ /* send the attach message */
+ usb_send_msg(dev, USB_MSG_ATTACH);
+ dprintf("ehci_attach: Attached port %d\n", p->index);
+ } else {
+ /* set connect status */
+ if (port->ctrl & EHCI_PRT_ECCS) {
+ port->ctrl &= ~EHCI_PRT_ECCS;
+ port->ctrl |= EHCI_PRT_ECSC;
+ }
+
+ dev = port->port.dev;
+ if (dev) {
+ /* send the detach message */
+ usb_send_msg(dev, USB_MSG_DETACH);
+ }
+ port->port.dev = NULL;
+ dprintf("ehci_attach: Detached port %d\n", p->index);
+ }
+
+ /* set port change detect only if port owner is zero */
+ if (!(port->ctrl & EHCI_PRT_PO) && (old_state != port->ctrl))
+ ehci_set_irq(ehci, EHCI_STS_PCD);
+}
+
+static void ehci_reset(void *opaque)
+{
+ EHCIState *ehci = opaque;
+ EHCIPort *port;
+ int i;
+#ifdef DEBUG_EHCI
+ uint8_t *pci_conf = ehci->dev.config;
+#endif
+
+ /* Set HCHalted bit to one */
+ ehci->status = EHCI_STS_HCH;
+ ehci->intr_enable = 0;
+ ehci->frame_index = 0;
+ ehci->segment = 0;
+ ehci->frame_list = 0;
+ ehci->async_next = 0;
+ ehci->configured_flag = 0;
+
+ for(i = 0; i < NB_PORTS; i++) {
+ port = &ehci->ports[i];
+ port->ctrl = EHCI_PRT_PO;
+ if (port->port.dev) {
+ ehci_attach(&port->port, port->port.dev);
+ }
+ }
+
+ ehci->async_state = EHCI_ASYNC_STATE_NOT_ACTIVE;
+ ehci->nak_state = EHCI_NAK_STATE_WAIT_FOR_HEAD;
+ ehci->pending_qh = 0;
+
+ /* Set HCReset bit to zero only when reset process is complete */
+ ehci->command = 0x80000;
+
+ dprintf("ehci_reset: [PCI Conf] CMD=0x%.4x CLS=0x%.2x LT=0x%.2x BAR=0x%.8x "
+ "FLADJ=0x%.2x\n", *(uint16_t *)(pci_conf + 0x04), pci_conf[0x0c],
+ pci_conf[0x0d], *(uint32_t *)(pci_conf + 0x10), pci_conf[0x61]);
+}
+
+static uint32_t ehci_mem_read(void *ptr, target_phys_addr_t addr)
+{
+ uint32_t ret = 0;
+ EHCIState *ehci = ptr;
+ addr -= ehci->mem_base;
+ int portnum;
+
+ if (addr >= 0x50 && addr < 0x50 + NB_PORTS * 4) {
+ portnum = (addr - 0x50) >> 2;
+ ret = ehci->ports[portnum].ctrl;
+ dprintf("ehci_read: port[%d] 0x%.8x\n", portnum, ret);
+ return ret;
+ }
+
+ switch (addr) {
+ case 0x00:
+ /* caplength & hciversion - read-only */
+ ret = 0x95000c;
+ dprintf("ehci_read: caplength/hciversion=0x%.8x\n", ret);
+ break;
+ case 0x04:
+ /* hcsparams - read-only */
+ ret = 0x2214;
+ dprintf("ehci_read: hcsparams=0x%.8x\n", ret);
+ break;
+ case 0x08:
+ /* hccparams - read-only */
+ ret = 0x12;
+ dprintf("ehci_read: hcsparams=0x%.8x\n", ret);
+ break;
+ case 0x0c:
+ ret = ehci->command;
+ dprintf("ehci_read: command=0x%.8x\n", ret);
+ break;
+ case 0x10:
+ ret = ehci->status;
+ dprintf("ehci_read: status=0x%.8x\n", ret);
+ break;
+ case 0x14:
+ ret = ehci->intr_enable;
+ dprintf("ehci_read: intr=0x%.8x\n", ret);
+ break;
+ case 0x18:
+ ret = ehci->frame_index;
+ dprintf("ehci_read: index=0x%.8x\n", ret);
+ break;
+ case 0x1c:
+ ret = ehci->segment;
+ dprintf("ehci_read: segment=0x%.8x\n", ret);
+ break;
+ case 0x20:
+ ret = ehci->frame_list;
+ dprintf("ehci_read: list=0x%.8x\n", ret);
+ break;
+ case 0x24:
+ ret = ehci->async_next;
+ dprintf("ehci_read: async=0x%.8x\n", ret);
+ break;
+ case 0x4c:
+ ret = ehci->configured_flag;
+ dprintf("ehci_read: cfg=0x%.8x\n", ret);
+ break;
+ default:
+ dprintf("ehci_read: Bad offset 0x%.8x\n", (int)addr);
+ }
+
+ return ret;
+}
+
+static void ehci_mem_write(void *ptr, target_phys_addr_t addr, uint32_t val)
+{
+ EHCIState *ehci = ptr;
+ addr -= ehci->mem_base;
+ EHCIPort *port;
+ USBDevice *dev;
+ uint32_t old_val;
+ int i, portnum;
+
+ if (addr >= 0x50 && addr < 0x50 + NB_PORTS * 4) {
+ portnum = (addr - 0x50) >> 2;
+ port = &ehci->ports[portnum];
+ old_val = port->ctrl;
+
+ port->ctrl &= ~EHCI_PORT_MASK;
+ port->ctrl |= (val & EHCI_PORT_MASK);
+
+ /* prevent enabling port but let disabling it */
+ if ((val & EHCI_PRT_PED) && !(old_val & EHCI_PRT_PED)) {
+ port->ctrl &= ~EHCI_PRT_PED;
+ }
+
+ /* if configured_flag is 0, make sure port owner is 1 */
+ if (!ehci->configured_flag && !(val & EHCI_PRT_PO)) {
+ port->ctrl |= EHCI_PRT_PO;
+ }
+
+ /* handle bits cleared by write */
+ port->ctrl &= ~(val & EHCI_PORT_WC_MASK);
+
+ dprintf("ehci_write: port[%d]=0x%.8x(0x%.8x) val=0x%.8x"
+ "(w=0x%.6x wc=0x%.2x) pr=%d(%d) status-hch=%d\n",
+ portnum, port->ctrl, old_val, val, val & EHCI_PORT_MASK,
+ val & EHCI_PORT_WC_MASK, BIT(val, EHCI_PRT_PR),
+ BIT(old_val, EHCI_PRT_PR), BIT(ehci->status, EHCI_STS_HCH));
+
+ if ((val & EHCI_PRT_PR) && !(old_val & EHCI_PRT_PR)) {
+ /* send reset on the USB bus */
+ for(i = 0; i < NB_PORTS; i++) {
+ port = &ehci->ports[i];
+ dev = port->port.dev;
+ if (dev) {
+ usb_send_msg(dev, USB_MSG_RESET);
+ /* enable only high-speed ports, only after reset */
+ if ( (dev->speed == USB_SPEED_HIGH) &&
+ !(port->ctrl & EHCI_PRT_PED)) {
+ port->ctrl |= (EHCI_PRT_PED | EHCI_PRT_PEDC);
+ } else if ((dev->speed != USB_SPEED_HIGH) &&
+ (port->ctrl & EHCI_PRT_PED)) {
+ port->ctrl &= ~EHCI_PRT_PED;
+ port->ctrl |= EHCI_PRT_PEDC;
+ }
+ }
+ }
+ /* set Port Reset bit to zero when reset is complete */
+ ehci->ports[portnum].ctrl &= ~EHCI_PRT_PR;
+ }
+
+ return;
+ }
+
+ switch (addr) {
+ case 0x0c:
+ dprintf("ehci_write: command=0x%.8x(0x%.8x) rs=%d(%d) hcreset=%d(%d) "
+ "status-hch=%d\n", val, ehci->command, BIT(val, EHCI_CMD_RS),
+ BIT(ehci->command, EHCI_CMD_RS), BIT(val, EHCI_CMD_HCRESET),
+ BIT(ehci->command, EHCI_CMD_HCRESET),
+ BIT(ehci->status, EHCI_STS_HCH));
+
+ old_val = ehci->command;
+ ehci->command = val & EHCI_COMMAND_MASK;
+
+ /* Run/Stop */
+ if ((val & EHCI_CMD_RS) && !(old_val & EHCI_CMD_RS)) {
+ /* start frame processing */
+ qemu_mod_timer(ehci->frame_timer, qemu_get_clock(vm_clock));
+ ehci->status &= ~EHCI_STS_HCH;
+ } else if (!(val & EHCI_CMD_RS)) {
+ /* FIXME: complete active transactions */
+ ehci->status |= EHCI_STS_HCH;
+ }
+
+ /* HC Reset - without usb reset on downstream ports */
+ if (val & EHCI_CMD_HCRESET) {
+ ehci_reset(ehci);
+ }
+
+ /* update periodic and async schedule status */
+ if (val & EHCI_CMD_PSE) {
+ ehci->status |= EHCI_STS_PSSTAT;
+ } else {
+ ehci->status &= ~EHCI_STS_PSSTAT;
+ }
+
+ if (val & EHCI_CMD_ASE) {
+ ehci->status |= EHCI_STS_ASS;
+ } else {
+ ehci->status &= ~EHCI_STS_ASS;
+ }
+ break;
+ case 0x10:
+ old_val = ehci->status;
+ /* software sets a bit to 0 in this register by writing 1 to it */
+ ehci->status &= ~(val & EHCI_STATUS_MASK);
+ dprintf("ehci_write: status=0x%.8x(0x%.8x) val=0x%.8x\n", ehci->status,
+ old_val, val);
+ ehci_update_irq(ehci);
+ break;
+ case 0x14:
+ dprintf("ehci_write: intr=0x%.8x(0x%.8x)\n", val, ehci->intr_enable);
+ ehci->intr_enable = val & EHCI_INTR_MASK;
+ break;
+ case 0x18:
+ dprintf("ehci_write: index=0x%.8x(0x%.8x)\n", val, ehci->frame_index);
+ ehci->frame_index = val & EHCI_FRAME_INDEX_MASK;
+ break;
+ case 0x1c:
+ dprintf("ehci_write: segment=0x%.8x(0x%.8x)\n", val, ehci->segment);
+ ehci->segment = val;
+ break;
+ case 0x20:
+ dprintf("ehci_write: list=0x%.8x(0x%.8x)\n", val, ehci->frame_list);
+ ehci->frame_list = val & EHCI_FRAME_LIST_MASK;
+ break;
+ case 0x24:
+ dprintf("ehci_write: async=0x%.8x(0x%.8x)\n", val, ehci->async_next);
+ ehci->async_next = val & EHCI_ASYNC_NEXT_MASK;
+ break;
+ case 0x4c:
+ dprintf("ehci_write: cfg=0x%.8x(0x%.8x)\n", val, ehci->configured_flag);
+ old_val = ehci->configured_flag;
+ ehci->configured_flag = val & EHCI_CONFIGURED_FLAG_MASK;
+ if (ehci->configured_flag != old_val) {
+ for(i = 0; i < NB_PORTS; i++) {
+ port = &ehci->ports[i];
+ if (ehci->configured_flag) {
+ port->ctrl &= ~EHCI_PRT_PO;
+ } else {
+ port->ctrl |= EHCI_PRT_PO;
+ }
+ }
+ }
+ break;
+ default:
+ dprintf("ehci_write: Bad offset 0x%.8x val=0x%.8x\n", (int)addr, val);
+ }
+}
+
+static CPUReadMemoryFunc *ehci_readfn[3]={
+ ehci_mem_read,
+ ehci_mem_read,
+ ehci_mem_read
+};
+
+static CPUWriteMemoryFunc *ehci_writefn[3]={
+ ehci_mem_write,
+ ehci_mem_write,
+ ehci_mem_write
+};
+
+/* FIXME: do something smart here except logging */
+static int ehci_handle_periodic_schedule(EHCIState *ehci,
+ uint32_t frame_index_mask)
+{
+ uint32_t frame_addr, link;
+ int typ;
+
+ frame_addr = ehci->frame_list |
+ ((ehci->frame_index & frame_index_mask) >> 1);
+
+ dprintf("handle_periodic: list=0x%.8x index=0x%.3x addr=0x%.8x\n",
+ ehci->frame_list, ehci->frame_index, frame_addr);
+
+ cpu_physical_memory_read(frame_addr, (uint8_t *)&link, 4);
+ le32_to_cpus(&link);
+ typ = EHCI_BM(link, LINK_TYP);
+
+ dprintf("handle_periodic: link=0x%.8x ptr=0x%.8x z=%d typ=%d t=%d\n", link,
+ link & EHCI_LINK_PTR, link & EHCI_LINK_Z, typ, link & EHCI_LINK_T);
+
+ if (link & EHCI_LINK_T) {
+ dprintf("handle_periodic: T-Bit=1\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+static int ehci_fetch_qh(EHCIState *ehci)
+{
+ ehci_qh *qh = &ehci->async_qh;
+ cpu_physical_memory_read(ehci->async_qh_addr, (uint8_t *)qh, sizeof(*qh));
+
+ /* QH H-bit=1 */
+ if (qh->info1 & EHCI_QH_H) {
+ /* update nak state */
+ if (ehci->nak_state == EHCI_NAK_STATE_WAIT_FOR_HEAD) {
+ ehci->nak_state = EHCI_NAK_STATE_DO_RELOAD;
+ } else if (ehci->nak_state == EHCI_NAK_STATE_DO_RELOAD) {
+ ehci->nak_state = EHCI_NAK_STATE_WAIT_FOR_START;
+ }
+
+ /* if reclamation==0, it's an empty list */
+ if (!(ehci->status & EHCI_STS_RECL)) {
+ dprintf("handle_async: empty list\n");
+ ehci->async_state = EHCI_ASYNC_STATE_SLEEPING;
+ /* set 10usec retry timer */
+ qemu_mod_timer(ehci->async_timer, qemu_get_clock(vm_clock) +
+ (ticks_per_sec / ASYNC_SLEEP_TIMER_FREQ));
+ return 1;
+ } else {
+ /* set reclamation=0 */
+ ehci->status &= ~EHCI_STS_RECL;
+ }
+ }
+
+ return 0;
+}
+
+static int ehci_advance_queue(EHCIState *ehci)
+{
+ ehci_qh *qh = &ehci->async_qh;
+ uint32_t curr_qtd, old_token;
+ ehci_qtd qtd;
+ int rl, eps, i;
+
+ if ((qh->token & EHCI_QH_TOTAL_BYTES_MASK) && !(qh->alt_next & EHCI_QH_T)) {
+ curr_qtd = qh->alt_next & EHCI_LINK_PTR;
+ } else if (!(qh->qtd_next & EHCI_QH_T)){
+ curr_qtd = qh->qtd_next & EHCI_LINK_PTR;
+ } else {
+ return 1;
+ }
+
+ cpu_physical_memory_read(curr_qtd, (uint8_t *)&qtd, sizeof(qtd));
+ if (qtd.token & EHCI_QH_STS_ACTIVE) {
+ qh->current = curr_qtd;
+ } else {
+ return 1;
+ }
+
+ /* perform overlay */
+ qh->qtd_next = qtd.next;
+ qh->alt_next = qtd.alt_next;
+
+ rl = EHCI_BM(qh->info1, QH_RL);
+ EHCI_SET_BM(qh->alt_next, QH_NAK_CNT, rl);
+
+ old_token = qh->token;
+ qh->token = qtd.token;
+
+ if (qh->info1 & EHCI_QH_DTC) {
+ if (qtd.token & EHCI_QH_DT) {
+ qh->token |= EHCI_QH_DT;
+ } else {
+ qh->token &= ~EHCI_QH_DT;
+ }
+ }
+
+ eps = EHCI_BM(qh->info1, QH_EPS);
+ if (eps == EHCI_QH_EPS_HIGH) {
+ if (old_token & EHCI_QH_STS_PING) {
+ qh->token |= EHCI_QH_STS_PING;
+ } else {
+ qh->token &= ~EHCI_QH_STS_PING;
+ }
+ }
+
+ for (i = 0; i < 5; i++) {
+ qh->buf[i] = qtd.buf[i];
+ }
+
+ qh->buf[1] &= ~EHCI_QH_C_PROG_MASK;
+ qh->buf[2] &= ~EHCI_QH_FRAME_TAG;
+
+ cpu_physical_memory_write(ehci->async_qh_addr, (uint8_t *)qh, sizeof(*qh));
+ return 0;
+}
+
+static void ehci_async_complete_packet(USBPacket *packet, void *opaque);
+
+static int ehci_exec_transaction(EHCIState *ehci)
+{
+ ehci_qh *qh = &ehci->async_qh;
+ USBDevice *dev;
+ const char *pidstr;
+ uint32_t curr_offset, buf_addr;
+ int dir, pid, rl = 0;
+ int max_len, total_bytes;
+ int len, len_done, len_todo;
+ int devaddr, devep;
+ int c_page, c_err, nak_cnt;
+ int dev_ret, ret, i;
+ int inc_page;
+
+ /* async transfer */
+ /* FIXME: handle interrupt transfers as well*/
+ if (!(qh->info2 & EHCI_QH_S_MASK)) {
+ /* reload nak counter if required */
+ if (ehci->nak_state == EHCI_NAK_STATE_DO_RELOAD) {
+ rl = EHCI_BM(qh->info1, QH_RL);
+ if (rl) {
+ EHCI_SET_BM(qh->alt_next, QH_NAK_CNT, rl);
+ }
+ }
+
+ if (rl && !EHCI_BM(qh->alt_next, QH_NAK_CNT)) {
+ return 1;
+ }
+ }
+
+ /* transaction is executed, so set reclamation=1 */
+ ehci->status |= EHCI_STS_RECL;
+
+ dir = EHCI_BM(qh->token, QH_PID_CODE);
+ switch (dir) {
+ case EHCI_QH_PID_OUT:
+ pid = USB_TOKEN_OUT;
+ pidstr = "OUT";
+ break;
+ case EHCI_QH_PID_IN:
+ pid = USB_TOKEN_IN;
+ pidstr = "IN";
+ break;
+ case EHCI_QH_PID_SETUP:
+ pid = USB_TOKEN_SETUP;
+ pidstr = "SETUP";
+ break;
+ default:
+ dprintf("exec_transaction: Bad direction %d\n", dir);
+ return 1;
+ }
+
+ devaddr = qh->info1 & EHCI_QH_DEV_ADDR;
+ devep = EHCI_BM(qh->info1, QH_ENDPT);
+
+ /* FIXME: handle zero length transactions */
+ c_page = EHCI_BM(qh->token, QH_C_PAGE);
+ if (c_page > 4) {
+ dprintf("exec_transaction: Bad c_page %d\n", c_page);
+ return 1;
+ }
+
+ curr_offset = EHCI_BM(qh->buf[0], QH_CURR_OFFSET);
+ buf_addr = (qh->buf[c_page] & EHCI_QH_BUFFER_POINTER) | curr_offset;
+
+ max_len = PAGE_SIZE - curr_offset + (4 - c_page) * PAGE_SIZE;
+ if (max_len > MAX_TX_SIZE) {
+ max_len = MAX_TX_SIZE;
+ }
+
+ total_bytes = EHCI_BM(qh->token, QH_TOTAL_BYTES);
+ if (total_bytes < max_len) {
+ len = total_bytes;
+ } else {
+ len = max_len;
+ }
+
+ /* if pending async call, jump to async return */
+ if (ehci->pending_qh)
+ goto async_return;
+
+ if (dir != EHCI_QH_PID_IN) {
+ if (curr_offset + len < PAGE_SIZE) {
+ cpu_physical_memory_read(buf_addr, ehci->usb_buf, len);
+ } else {
+ /* handle transaction that spans a page boundary */
+ len_done = PAGE_SIZE - curr_offset;
+ cpu_physical_memory_read(buf_addr, ehci->usb_buf, len_done);
+ inc_page = 1;
+
+ while (len_done < len) {
+ /* update current page */
+ c_page++;
+ EHCI_SET_BM(qh->token, QH_C_PAGE, c_page);
+ /* zero offset for the second part */
+ buf_addr = qh->buf[c_page] & EHCI_QH_BUFFER_POINTER;
+
+ if (len - len_done < PAGE_SIZE) {
+ len_todo = len - len_done;
+ inc_page = 0;
+ } else {
+ len_todo = PAGE_SIZE;
+ }
+ cpu_physical_memory_read(buf_addr, ehci->usb_buf + len_done,
+ len_todo);
+ len_done += len_todo;
+ }
+
+ /* handle the case of exact page completion */
+ if (inc_page && (c_page < 4)) {
+ c_page++;
+ EHCI_SET_BM(qh->token, QH_C_PAGE, c_page);
+ }
+ }
+ }
+
+#ifdef DEBUG_PACKET
+ dprintf("exec_transaction: frame=%d pid=%s devaddr=%d devep=%d len=%d "
+ "max_len=%d total_bytes=%d\n", ehci->frame_index, pidstr, devaddr,
+ devep, len, max_len, total_bytes);
+
+ if (dir != EHCI_QH_PID_IN) {
+ dprintf("exec_transaction: c_page=%d buf_addr=0x%.8x usb_buf=", c_page,
+ buf_addr);
+ for(i = 0; i < len; i++) {
+ dprintf(" %.2x", ehci->usb_buf[i]);
+ }
+ dprintf("\n");
+ }
+#endif
+
+ ehci->usb_packet.pid = pid;
+ ehci->usb_packet.devaddr = devaddr;
+ ehci->usb_packet.devep = devep;
+ ehci->usb_packet.data = ehci->usb_buf;
+ ehci->usb_packet.len = len;
+ ehci->usb_packet.complete_cb = ehci_async_complete_packet;
+ ehci->usb_packet.complete_opaque = ehci;
+
+ dev_ret = USB_RET_NODEV;
+ for (i = 0; (i < NB_PORTS) && (dev_ret == USB_RET_NODEV); i++) {
+ if (ehci->ports[i].ctrl & EHCI_PRT_PED) {
+ dev = ehci->ports[i].port.dev;
+ dev_ret = dev->handle_packet(dev, &ehci->usb_packet);
+ }
+ }
+
+#ifdef DEBUG_PACKET
+ dprintf("exec_transaction: dev_ret=%d\n", dev_ret);
+#endif
+
+ if (dev_ret == USB_RET_ASYNC) {
+ ehci->pending_qh = 1;
+ return 2;
+ }
+
+async_return:
+ if (ehci->pending_qh) {
+ dev_ret = ehci->pending_qh;
+ }
+
+ if (dev_ret >= 0) {
+ if (dir == EHCI_QH_PID_IN) {
+ if (dev_ret <= len) {
+ len = dev_ret;
+ } else {
+ dev_ret = USB_RET_BABBLE;
+ }
+ }
+
+ /* decrease actual length from total bytes to transfer */
+ total_bytes -= len;
+ EHCI_SET_BM(qh->token, QH_TOTAL_BYTES, total_bytes);
+
+ /* handle interrupt on completion */
+ if (BIT(qh->token, EHCI_QH_IOC) && !total_bytes) {
+ ehci->status |= EHCI_STS_USBINT;
+ }
+
+ /* update data toggle bit */
+ if (qh->token & EHCI_QH_DT) {
+ qh->token &= ~EHCI_QH_DT;
+ } else {
+ qh->token |= EHCI_QH_DT;
+ }
+
+ if (dir == EHCI_QH_PID_IN) {
+ /* FIXME: merge code with the cpu_physical_memory_reads */
+ if (curr_offset + len < PAGE_SIZE) {
+ cpu_physical_memory_write(buf_addr, ehci->usb_buf, len);
+ } else {
+ /* handle transaction that spans a page boundary */
+ len_done = PAGE_SIZE - curr_offset;
+ cpu_physical_memory_write(buf_addr, ehci->usb_buf, len_done);
+ inc_page = 1;
+
+ while (len_done < len) {
+ /* update current page */
+ c_page++;
+ EHCI_SET_BM(qh->token, QH_C_PAGE, c_page);
+ /* zero offset for the second part */
+ buf_addr = qh->buf[c_page] & EHCI_QH_BUFFER_POINTER;
+
+ if (len - len_done < PAGE_SIZE) {
+ len_todo = len - len_done;
+ inc_page = 0;
+ } else {
+ len_todo = PAGE_SIZE;
+ }
+ cpu_physical_memory_write(buf_addr, ehci->usb_buf + len_done,
+ len_todo);
+ len_done += len_todo;
+ }
+
+ /* handle the case of exact page completion */
+ if (inc_page && (c_page < 4)) {
+ c_page++;
+ EHCI_SET_BM(qh->token, QH_C_PAGE, c_page);
+ }
+ }
+#ifdef DEBUG_PACKET
+ dprintf("exec_transaction: usb_buf=");
+ for (i = 0; i < len; i++)
+ dprintf(" %.2x", ehci->usb_buf[i]);
+ dprintf("\n");
+#endif
+ }
+
+ /* update current offset */
+ curr_offset = (curr_offset + len) % PAGE_SIZE;
+ EHCI_SET_BM(qh->buf[0], QH_CURR_OFFSET, curr_offset);
+ }
+
+ if (dev_ret >= 0) {
+ /* FIXME: handle successful transaction */
+ ret = 0;
+
+ /* set the active bit according to event */
+ if (!total_bytes ||
+ ((dir == EHCI_QH_PID_IN) && (len < max_len))) {
+ EHCI_SET_BM(qh->token, QH_STATUS, 0);
+ } else {
+ EHCI_SET_BM(qh->token, QH_STATUS, EHCI_QH_STS_ACTIVE);
+ }
+ } else {
+ /* FIXME: return the same value for all errors? same handling? */
+ ret = 1;
+
+ /* decrement error counter */
+ c_err = EHCI_BM(qh->token, QH_CERR);
+ c_err--;
+ EHCI_SET_BM(qh->token, QH_CERR, c_err);
+
+ /* active bit is set to 0 for all the errors */
+ switch (dev_ret) {
+ case USB_RET_NODEV:
+ dprintf("exec_transaction: NODEV\n");
+ EHCI_SET_BM(qh->token, QH_STATUS, EHCI_QH_STS_XACT);
+ break;
+ case USB_RET_NAK:
+ if ((dir == EHCI_QH_PID_IN) && (EHCI_BM(qh->info1, QH_RL))) {
+ /* decrement nak counter */
+ nak_cnt = EHCI_BM(qh->alt_next, QH_NAK_CNT);
+ nak_cnt--;
+ EHCI_SET_BM(qh->alt_next, QH_NAK_CNT, nak_cnt);
+ }
+ dprintf("exec_transaction: got NAK\n");
+ break;
+ case USB_RET_STALL:
+ dprintf("exec_transaction: got STALL\n");
+ EHCI_SET_BM(qh->token, QH_STATUS, EHCI_QH_STS_HALT);
+ break;
+ case USB_RET_BABBLE:
+ dprintf("exec_transaction: got BABBLE\n");
+ EHCI_SET_BM(qh->token, QH_STATUS, EHCI_QH_STS_BABBLE |
+ EHCI_QH_STS_HALT);
+ break;
+ default:
+ dprintf("exec_transaction: Bad device response %d\n", dev_ret);
+ EHCI_SET_BM(qh->token, QH_STATUS, EHCI_QH_STS_XACT);
+ break;
+ }
+
+ /* if error counter is 0, set halted bit to 1 */
+ if (c_err == 0) {
+ dprintf("exec_transaction: c_err = 0\n");
+ qh->token |= EHCI_QH_STS_HALT;
+ }
+ }
+
+ cpu_physical_memory_write(ehci->async_qh_addr, (uint8_t *)qh, sizeof(*qh));
+ return ret;
+}
+
+static int ehci_write_back_qtd(EHCIState *ehci)
+{
+ ehci_qh *qh = &ehci->async_qh;
+ uint32_t qtd_token;
+
+ cpu_physical_memory_read(qh->current + offsetof(ehci_qtd, token),
+ (uint8_t *)&qtd_token, sizeof(qtd_token));
+ qtd_token &= ~EHCI_QTD_WRITE_BACK;
+ qtd_token |= (qh->token & EHCI_QTD_WRITE_BACK);
+ cpu_physical_memory_write(qh->current + offsetof(ehci_qtd, token),
+ (uint8_t *)&qtd_token, sizeof(qtd_token));
+ return 0;
+}
+
+static void ehci_async_complete_packet(USBPacket *packet, void *opaque)
+{
+ EHCIState *ehci = opaque;
+ ehci_qh *qh = &ehci->async_qh;
+
+ /* FIXME: keep packet len elsewhere */
+ ehci->pending_qh = packet->len;
+ if (!ehci_exec_transaction(ehci)) {
+ if (!ACTIVE_QH(qh)) {
+ ehci_write_back_qtd(ehci);
+ }
+ ehci->async_qh_addr = qh->next & EHCI_LINK_PTR;
+ }
+
+ ehci->pending_qh = 0;
+}
+
+static int ehci_qh_state_machine(EHCIState *ehci)
+{
+ ehci_qh *qh = &ehci->async_qh;
+ int state = EHCI_QH_STATE_FETCH_QH;
+ int ret = 0;
+
+ while (state != EHCI_QH_STATE_DONE) {
+ switch (state) {
+ case EHCI_QH_STATE_FETCH_QH:
+ ret = ehci_fetch_qh(ehci);
+ if (ret) {
+ state = EHCI_QH_STATE_DONE;
+ } else if (!HALTED_QH(qh)) {
+ if (ACTIVE_QH(qh)) {
+ state = EHCI_QH_STATE_EXEC_TRANSACTION;
+ } else {
+ state = EHCI_QH_STATE_ADVANCE_QUEUE;
+ }
+ } else {
+ state = EHCI_QH_STATE_FOLLOW_QHLP;
+ }
+ break;
+ case EHCI_QH_STATE_ADVANCE_QUEUE:
+ ret = ehci_advance_queue(ehci);
+ if (!ret && ACTIVE_QH(qh)) {
+ state = EHCI_QH_STATE_EXEC_TRANSACTION;
+ } else {
+ state = EHCI_QH_STATE_FOLLOW_QHLP;
+ }
+ break;
+ case EHCI_QH_STATE_EXEC_TRANSACTION:
+ ret = ehci_exec_transaction(ehci);
+ if (ret) {
+ state = EHCI_QH_STATE_DONE;
+ } else if (ACTIVE_QH(qh)) {
+ state = EHCI_QH_STATE_FOLLOW_QHLP;
+ } else {
+ state = EHCI_QH_STATE_WRITE_BACK_QTD;
+ }
+ break;
+ case EHCI_QH_STATE_WRITE_BACK_QTD:
+ ehci_write_back_qtd(ehci);
+ state = EHCI_QH_STATE_FOLLOW_QHLP;
+ break;
+ case EHCI_QH_STATE_FOLLOW_QHLP:
+ ehci->async_qh_addr = qh->next & EHCI_LINK_PTR;
+ state = EHCI_QH_STATE_DONE;
+ break;
+ case EHCI_QH_STATE_DONE:
+ break;
+ default:
+ dprintf("xxx: unknown state %x\n", state);
+ }
+ }
+
+ return ret;
+}
+
+/* FIXME: detect & exit on EOF1,2 */
+static int ehci_handle_async_schedule(EHCIState *ehci)
+{
+ int ret;
+
+ ehci->async_state = EHCI_ASYNC_STATE_ACTIVE;
+
+ /* on async schedule start, set reclamation=1 */
+ ehci->status |= EHCI_STS_RECL;
+
+ /* on start event, set nak_state to wait for QH with H-bit=1 */
+ ehci->nak_state = EHCI_NAK_STATE_WAIT_FOR_HEAD;
+
+ ehci->async_qh_addr = ehci->async_next & EHCI_LINK_PTR;
+
+ /* FIXME: re-enter qh-state-machine while (ehci->command & EHCI_CMD_ASE)
+ * and microframe is not over yet... */
+ ret = ehci_qh_state_machine(ehci);
+
+ /* retain last accessed QH's QH.next for next visit */
+ if (ret != 2) {
+ ehci->async_next = ehci->async_qh.next & EHCI_LINK_PTR;
+ }
+
+ /* FIXME: set async advance interrupt */
+ if (ehci->command & EHCI_CMD_IAAD) {
+ ehci->status |= EHCI_STS_IAA;
+ ehci->command &= ~EHCI_CMD_IAAD;
+ }
+
+ return 0;
+}
+
+static void ehci_async_timer(void *opaque)
+{
+ EHCIState *ehci = opaque;
+ ehci->async_state = EHCI_ASYNC_STATE_ACTIVE;
+
+ /* when async timer expires, set reclamation=1 */
+ ehci->status |= EHCI_STS_RECL;
+
+ /* on start event, set nak_state to wait for QH with H-bit=1 */
+ ehci->nak_state = EHCI_NAK_STATE_WAIT_FOR_HEAD;
+}
+
+static void ehci_frame_timer(void *opaque)
+{
+ EHCIState *ehci = opaque;
+ uint32_t frame_index_mask, rollover_mask;
+ uint32_t old_index;
+ int64_t expire_time;
+ int fls, itc;
+
+ /* FIXME: use EOF1,2 instead as end of microframe? */
+ if (ehci->async_state == EHCI_ASYNC_STATE_SLEEPING) {
+ qemu_del_timer(ehci->async_timer);
+ }
+ ehci->async_state = EHCI_ASYNC_STATE_NOT_ACTIVE;
+
+ if (ehci->command & EHCI_CMD_RS) {
+ /* prepare the timer for the next frame */
+ expire_time = qemu_get_clock(vm_clock) +
+ (ticks_per_sec / MICROFRAME_TIMER_FREQ);
+ qemu_mod_timer(ehci->frame_timer, expire_time);
+ } else {
+ qemu_del_timer(ehci->frame_timer);
+ /* set hchalted bit in status */
+ ehci->status |= EHCI_STS_HCH;
+ return;
+ }
+
+ if (ehci->frame_index == 0) {
+ dprintf("ehci_frame_timer: index=0x%.4x command=0x%.8x status=0x%.8x "
+ "rs=%d hch=%d psstat=%d ass=%d\n", ehci->frame_index,
+ ehci->command, ehci->status, BIT(ehci->command, EHCI_CMD_RS),
+ BIT(ehci->status, EHCI_STS_HCH),
+ BIT(ehci->status, EHCI_STS_PSSTAT),
+ BIT(ehci->status, EHCI_STS_ASS));
+ }
+
+ /* FIXME: Complete the previous frame */
+ old_index = ehci->frame_index;
+ ehci->frame_index = (ehci->frame_index + 1) & EHCI_FRAME_INDEX_MASK;
+
+ fls = EHCI_BM(ehci->command, CMD_FLS);
+ switch (fls) {
+ case 0:
+ frame_index_mask = 0x1ff8;
+ rollover_mask = 0x2000;
+ break;
+ case 1:
+ frame_index_mask = 0xff8;
+ rollover_mask = 0x1000;
+ break;
+ case 2:
+ frame_index_mask = 0x7f8;
+ rollover_mask = 0x800;
+ break;
+ default:
+ dprintf("ehci_frame_timer: Bad FLS %x\n", fls);
+ return;
+ }
+
+ /* frame list rollover - not delayed to the next interrupt threshold */
+ if ((ehci->frame_index & rollover_mask) != (old_index & rollover_mask)) {
+ dprintf("ehci_frame_timer: index rollover %d->%d mask 0x%.4x 0x%.4x\n",
+ old_index & frame_index_mask, ehci->frame_index &
+ frame_index_mask, frame_index_mask, rollover_mask);
+ ehci_set_irq(ehci, EHCI_STS_FLR);
+ }
+
+ /* Handle Periodic Frame List */
+ if (ehci->status & EHCI_STS_PSSTAT) {
+ ehci_handle_periodic_schedule(ehci, frame_index_mask);
+ }
+
+ /* Handle Async List */
+ if (ehci->status & EHCI_STS_ASS) {
+ /* FIXME: handle more than one pending qh */
+ if (ehci->pending_qh == 0) {
+ ehci_handle_async_schedule(ehci);
+ }
+ }
+
+ /* interrupt threshold control */
+ itc = EHCI_BM(ehci->command, CMD_ITC);
+ if (itc && !(ehci->frame_index % itc)) {
+ ehci_update_irq(ehci);
+ }
+}
+
+static void ehci_map(PCIDevice *pci_dev, int region_num,
+ uint32_t addr, uint32_t size, int type)
+{
+ EHCIState *ehci = (EHCIState *)pci_dev;
+ ehci->mem_base = addr;
+ cpu_register_physical_memory(addr, size, ehci->mem);
+}
+
+void usb_ehci_init(PCIBus *bus, int devfn)
+{
+ EHCIState *ehci;
+ int i;
+
+ ehci = (EHCIState *)pci_register_device(bus, "USB-EHCI", sizeof(EHCIState),
+ devfn, NULL, NULL);
+
+ if (ehci == NULL) {
+ dprintf("usb_ehci_init: Failed to register PCI device\n");
+ return;
+ }
+
+ /* Reset PCI configuration */
+ memcpy(ehci->dev.config, ehci_pci_config, sizeof(ehci_pci_config));
+ memcpy(ehci->dev.config + 0x60, ehci_specific_pci_config,
+ sizeof(ehci_specific_pci_config));
+ memcpy(ehci->dev.config + 0xdc, ehci_power_mgmt_config,
+ sizeof(ehci_power_mgmt_config));
+
+ ehci->mem = cpu_register_io_memory(0, ehci_readfn, ehci_writefn, ehci);
+
+ for(i = 0; i < NB_PORTS; i++) {
+ qemu_register_usb_port(&ehci->ports[i].port, ehci, i, ehci_attach);
+ }
+
+ ehci->frame_timer = qemu_new_timer(vm_clock, ehci_frame_timer, ehci);
+ ehci->async_timer = qemu_new_timer(vm_clock, ehci_async_timer, ehci);
+
+ /* Reset capability and operational registers */
+ qemu_register_reset(ehci_reset, ehci);
+ ehci_reset(ehci);
+
+ pci_register_io_region(&ehci->dev, 0, 0x400, PCI_ADDRESS_SPACE_MEM,
+ ehci_map);
+}
+
diff -u -d -p -P -r qemu-head/Makefile.target qemu-merge/Makefile.target
--- qemu-head/Makefile.target 2008-01-30 10:57:54.000005000 +0200
+++ qemu-merge/Makefile.target 2008-01-30 11:02:35.472390081 +0200
@@ -434,7 +434,7 @@ endif
VL_OBJS+= lsi53c895a.o
# USB layer
-VL_OBJS+= usb-ohci.o
+VL_OBJS+= usb-ohci.o usb-ehci.o
# EEPROM emulation
VL_OBJS += eeprom93xx.o
diff -u -d -p -P -r qemu-head/usb-linux.c qemu-merge/usb-linux.c
--- qemu-head/usb-linux.c 2008-01-07 14:03:49.000000000 +0200
+++ qemu-merge/usb-linux.c 2008-01-07 17:49:25.000000000 +0200
@@ -52,7 +52,23 @@ static int usb_host_find_device(int *pbu
//#define DEBUG
//#define DEBUG_ISOCH
-//#define USE_ASYNCIO
+
+#ifdef DEBUG
+#define dprintf printf
+#else
+#define dprintf(...)
+#endif
+
+/* Defines that async i/o is used instead of blocking ioctls */
+#define USE_ASYNCIO
+
+/* Defines that only EHCI emulation is used - no UHCI/OHCI */
+//#define USE_EHCI
+
+/* Bulk transfers async i/o is currently supported only by EHCI emulation */
+#if defined(USE_EHCI) && defined(USE_ASYNCIO)
+#define USE_EHCI_BULK_ASYNCIO
+#endif
#define USBDEVFS_PATH "/proc/bus/usb"
#define PRODUCT_NAME_SZ 32
@@ -155,10 +171,8 @@ static int usb_host_update_interfaces(US
i += dev_descr_len;
while (i < dev->descr_len) {
-#ifdef DEBUG
- printf("i is %d, descr_len is %d, dl %d, dt %d\n", i, dev->descr_len,
- dev->descr[i], dev->descr[i+1]);
-#endif
+ dprintf("i is %d, descr_len is %d, dl %d, dt %d\n", i, dev->descr_len,
+ dev->descr[i], dev->descr[i+1]);
if (dev->descr[i+1] != USB_DT_CONFIG) {
i += dev->descr[i];
continue;
@@ -208,11 +222,8 @@ static int usb_host_update_interfaces(US
}
}
-#ifdef DEBUG
- printf("usb_host: %d interfaces claimed for configuration %d\n",
- nb_interfaces, configuration);
-#endif
-
+ dprintf("usb_host: %d interfaces claimed for configuration %d\n",
+ nb_interfaces, configuration);
return 1;
}
@@ -262,10 +273,8 @@ static int usb_host_handle_control(USBDe
ret = ioctl(s->fd, USBDEVFS_SETINTERFACE, &si);
usb_linux_update_endp_table(s);
} else if (request == (DeviceOutRequest | USB_REQ_SET_CONFIGURATION)) {
-#ifdef DEBUG
- printf("usb_host_handle_control: SET_CONFIGURATION request - "
- "config %d\n", value & 0xff);
-#endif
+ dprintf("usb_host_handle_control: SET_CONFIGURATION request - "
+ "config %d\n", value & 0xff);
if (s->configuration != (value & 0xff)) {
s->configuration = (value & 0xff);
intf_update_required = 1;
@@ -292,9 +301,7 @@ static int usb_host_handle_control(USBDe
}
} else {
if (intf_update_required) {
-#ifdef DEBUG
- printf("usb_host_handle_control: updating interfaces\n");
-#endif
+ dprintf("usb_host_handle_control: updating interfaces\n");
usb_host_update_interfaces(s, value & 0xff);
}
return ret;
@@ -306,9 +313,9 @@ static int usb_host_handle_isoch(USBDevi
static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
{
USBHostDevice *s = (USBHostDevice *)dev;
- struct usbdevfs_bulktransfer bt;
- int ret;
+ struct usbdevfs_urb *urb, *purb = NULL;
uint8_t devep = p->devep;
+ int ret;
if (s->endp_table[p->devep - 1].type == USBDEVFS_URB_TYPE_ISO) {
return usb_host_handle_isoch(dev, p);
@@ -318,25 +325,54 @@ static int usb_host_handle_data(USBDevic
config descriptor */
if (p->pid == USB_TOKEN_IN)
devep |= 0x80;
- bt.ep = devep;
- bt.len = p->len;
- bt.timeout = 50;
- bt.data = p->data;
- ret = ioctl(s->fd, USBDEVFS_BULK, &bt);
- if (ret < 0) {
- switch(errno) {
- case ETIMEDOUT:
- return USB_RET_NAK;
- case EPIPE:
- default:
-#ifdef DEBUG
- printf("handle_data: errno=%d\n", errno);
+
+ urb = qemu_mallocz(sizeof(struct usbdevfs_urb));
+ if (!urb) {
+ printf("usb_host_handle_data: malloc failed\n");
+ return 0;
+ }
+
+ memset(urb, 0, sizeof(struct usbdevfs_urb));
+ urb->type = USBDEVFS_URB_TYPE_BULK;
+ urb->endpoint = devep;
+ urb->buffer = p->data;
+ urb->buffer_length = p->len;
+#ifdef USE_EHCI_BULK_ASYNCIO
+ urb->signr = SIG_ISOCOMPLETE;
+ urb->usercontext = s;
#endif
- return USB_RET_STALL;
+
+ ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
+ if (ret == 0) {
+ if (!add_pending_urb(urb)) {
+ printf("usb_host_handle_data: add_pending_urb failed %p\n", urb);
}
} else {
- return ret;
+ printf("usb_host_handle_data: SUBMITURB ioctl=%d errno=%d\n", ret,
+ errno);
+ qemu_free(urb);
+ return 0;
}
+
+#ifdef USE_EHCI_BULK_ASYNCIO
+ s->packet = p;
+ return USB_RET_ASYNC;
+#else
+ /* FIXME: NDELAY */
+ ret = ioctl(s->fd, USBDEVFS_REAPURB, &purb);
+ if (ret == 0) {
+ if (del_pending_urb(purb)) {
+ ret = purb->actual_length;
+ qemu_free(purb);
+ } else {
+ printf("usb_host_handle_data: del_pending_urb failed %p\n", purb);
+ }
+ } else {
+ printf("usb_host_handle_data: REAPURB ioctl=%d errno=%d\n", ret, errno);
+ ret = 0;
+ }
+ return ret;
+#endif
}
#ifdef USE_ASYNCIO
@@ -344,18 +380,25 @@ static void urb_completion_pipe_read(voi
{
USBHostDevice *s = opaque;
USBPacket *p = s->packet;
- PendingURB *pending_urb = NULL;
- struct usbdevfs_urb *purb = NULL;
- int len, ret;
+ PendingURB *pending_urb, *urb_eater;
+ struct usbdevfs_urb *purb, *urb;
+ int urb_size = sizeof(pending_urb);
+ int ret;
- len = read(s->pipe_fds[0], &pending_urb, sizeof(pending_urb));
- if (len != sizeof(pending_urb)) {
- printf("urb_completion: error reading pending_urb, len=%d\n", len);
+ if (urb_size == read(s->pipe_fds[0], &pending_urb, urb_size)) {
+ /* if there are any other pending urbs in the pipe - eat them up */
+ while (urb_size == read(s->pipe_fds[0], &urb_eater, urb_size)) {
+ del_pending_urb(urb_eater->urb);
+ s->urbs_ready++;
+ }
+ } else {
+ printf("urb_completion: error reading pending_urb\n");
return;
}
/* FIXME: handle pending_urb->status */
- del_pending_urb(pending_urb->urb);
+ urb = pending_urb->urb;
+ del_pending_urb(urb);
if (!p) {
s->urbs_ready++;
@@ -369,12 +412,10 @@ static void urb_completion_pipe_read(voi
return;
}
-#ifdef DEBUG_ISOCH
- if (purb == pending_urb->urb) {
- printf("urb_completion: urb mismatch reaped=%p pending=%p\n",
- purb, urb);
+ if (purb != urb) {
+ dprintf("urb_completion: urb mismatch reaped=%p pending=%p\n",
+ purb, urb);
}
-#endif
p->len = purb->actual_length;
usb_packet_complete(p);
@@ -608,9 +649,6 @@ USBDevice *usb_host_device_open(const ch
if (!dev)
goto fail;
-#ifdef DEBUG_ISOCH
- printf("usb_host_device_open %s\n", devname);
-#endif
if (usb_host_find_device(&bus_num, &addr,
product_name, sizeof(product_name),
devname) < 0)
@@ -654,10 +692,7 @@ USBDevice *usb_host_device_open(const ch
goto fail;
}
-#ifdef DEBUG
- printf("host USB device %d.%d grabbed\n", bus_num, addr);
-#endif
-
+ dprintf("host USB device %d.%d grabbed\n", bus_num, addr);
ret = usb_linux_update_endp_table(dev);
if (ret)
goto fail;
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [kvm-devel] [Qemu-devel] [PATCH] USB 2.0 EHCI emulation
2008-01-30 10:03 ` [Qemu-devel] [kvm-devel] [PATCH] USB 2.0 EHCI emulation Arnon Gilboa
@ 2008-02-28 19:46 ` Gerb Stralko
2008-02-29 7:33 ` Arnon Gilboa
0 siblings, 1 reply; 5+ messages in thread
From: Gerb Stralko @ 2008-02-28 19:46 UTC (permalink / raw)
To: Arnon Gilboa; +Cc: kvm-devel, qemu-devel
> Attached is a repost of the preliminary patch implementing USB 2.0 EHCI
> emulation.
I want to start testing your patches for the EHCI stuff. Do i need
to enable anything inorder to get EHCI emulation working after
applying your patch?
Unfortunately, with this patch it doesn't work for me. My guest host
(windows vista) still became really slow when I add the a usb device.
>
> Waiting for your comments,
> Arnon
>
Thanks,
Jerry
^ permalink raw reply [flat|nested] 5+ messages in thread
* RE: [kvm-devel] [Qemu-devel] [PATCH] USB 2.0 EHCI emulation
2008-02-28 19:46 ` [kvm-devel] [Qemu-devel] " Gerb Stralko
@ 2008-02-29 7:33 ` Arnon Gilboa
2008-02-29 14:17 ` Gerb Stralko
0 siblings, 1 reply; 5+ messages in thread
From: Arnon Gilboa @ 2008-02-29 7:33 UTC (permalink / raw)
To: Gerb Stralko; +Cc: kvm-devel, qemu-devel
In hw/pc.c, replace usb_uhci_piix3_init(pci_bus, piix3_devfn + 2);
With usb_ehci_init(pci_bus, piix3_devfn + 2);
Note my comments on the original post:
-tested on XP guest
-does not support ISO transfers
-timing issues
-----Original Message-----
From: Gerb Stralko [mailto:gerb.stralko@gmail.com]
Sent: Thursday, February 28, 2008 9:46 PM
To: Arnon Gilboa
Cc: qemu-devel@nongnu.org; kvm-devel@lists.sourceforge.net
Subject: Re: [kvm-devel] [Qemu-devel] [PATCH] USB 2.0 EHCI emulation
> Attached is a repost of the preliminary patch implementing USB 2.0
> EHCI emulation.
I want to start testing your patches for the EHCI stuff. Do i need
to enable anything inorder to get EHCI emulation working after applying
your patch?
Unfortunately, with this patch it doesn't work for me. My guest host
(windows vista) still became really slow when I add the a usb device.
>
> Waiting for your comments,
> Arnon
>
Thanks,
Jerry
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [kvm-devel] [Qemu-devel] [PATCH] USB 2.0 EHCI emulation
2008-02-29 7:33 ` Arnon Gilboa
@ 2008-02-29 14:17 ` Gerb Stralko
2008-03-01 21:05 ` Arnon Gilboa
0 siblings, 1 reply; 5+ messages in thread
From: Gerb Stralko @ 2008-02-29 14:17 UTC (permalink / raw)
To: Arnon Gilboa; +Cc: kvm-devel, qemu-devel
On Fri, Feb 29, 2008 at 2:33 AM, Arnon Gilboa <arnon.gilboa@qumranet.com> wrote:
> In hw/pc.c, replace usb_uhci_piix3_init(pci_bus, piix3_devfn + 2);
> With usb_ehci_init(pci_bus, piix3_devfn + 2);
With these changes.. I can't add the usb devices anymore to a Windows
XP (32 bit).
This is the command i use to start kvm:
/usr/local/bin/kvm/qemu-system-x86_64 -localtime -m 512 -usb -hda win32xp.img
To add usb device i normally go to the qemu console and type:
info usbhost
<find the number for my device i want to connect to>
usb_add host:03f0:01cda
But with your patch, when i try to add a usb device i get:
Could not add 'USB device host:03f0:01cda'
Since i'm using EHCI emulation, do i need to add usb devices in a
different way? Or should it work exactly the same way?
Thanks,
Jerry
> Note my comments on the original post:
> -tested on XP guest
> -does not support ISO transfers
> -timing issues
>
>
>
> -----Original Message-----
> From: Gerb Stralko [mailto:gerb.stralko@gmail.com]
> Sent: Thursday, February 28, 2008 9:46 PM
> To: Arnon Gilboa
> Cc: qemu-devel@nongnu.org; kvm-devel@lists.sourceforge.net
> Subject: Re: [kvm-devel] [Qemu-devel] [PATCH] USB 2.0 EHCI emulation
>
> > Attached is a repost of the preliminary patch implementing USB 2.0
> > EHCI emulation.
>
> I want to start testing your patches for the EHCI stuff. Do i need
> to enable anything inorder to get EHCI emulation working after applying
> your patch?
>
> Unfortunately, with this patch it doesn't work for me. My guest host
> (windows vista) still became really slow when I add the a usb device.
> >
> > Waiting for your comments,
> > Arnon
> >
>
> Thanks,
>
> Jerry
>
^ permalink raw reply [flat|nested] 5+ messages in thread
* RE: [kvm-devel] [Qemu-devel] [PATCH] USB 2.0 EHCI emulation
2008-02-29 14:17 ` Gerb Stralko
@ 2008-03-01 21:05 ` Arnon Gilboa
0 siblings, 0 replies; 5+ messages in thread
From: Arnon Gilboa @ 2008-03-01 21:05 UTC (permalink / raw)
To: qemu-devel
Can you give me some details about the device?
-----Original Message-----
From: qemu-devel-bounces+arnong=qumranet.com@nongnu.org
[mailto:qemu-devel-bounces+arnong=qumranet.com@nongnu.org] On Behalf Of
Gerb Stralko
Sent: Friday, February 29, 2008 4:17 PM
To: Arnon Gilboa
Cc: kvm-devel@lists.sourceforge.net; qemu-devel@nongnu.org
Subject: Re: [kvm-devel] [Qemu-devel] [PATCH] USB 2.0 EHCI emulation
On Fri, Feb 29, 2008 at 2:33 AM, Arnon Gilboa
<arnon.gilboa@qumranet.com> wrote:
> In hw/pc.c, replace usb_uhci_piix3_init(pci_bus, piix3_devfn + 2);
> With usb_ehci_init(pci_bus, piix3_devfn + 2);
With these changes.. I can't add the usb devices anymore to a Windows XP
(32 bit).
This is the command i use to start kvm:
/usr/local/bin/kvm/qemu-system-x86_64 -localtime -m 512 -usb -hda
win32xp.img
To add usb device i normally go to the qemu console and type:
info usbhost
<find the number for my device i want to connect to> usb_add
host:03f0:01cda
But with your patch, when i try to add a usb device i get:
Could not add 'USB device host:03f0:01cda'
Since i'm using EHCI emulation, do i need to add usb devices in a
different way? Or should it work exactly the same way?
Thanks,
Jerry
> Note my comments on the original post:
> -tested on XP guest
> -does not support ISO transfers
> -timing issues
>
>
>
> -----Original Message-----
> From: Gerb Stralko [mailto:gerb.stralko@gmail.com]
> Sent: Thursday, February 28, 2008 9:46 PM
> To: Arnon Gilboa
> Cc: qemu-devel@nongnu.org; kvm-devel@lists.sourceforge.net
> Subject: Re: [kvm-devel] [Qemu-devel] [PATCH] USB 2.0 EHCI emulation
>
> > Attached is a repost of the preliminary patch implementing USB 2.0
> > EHCI emulation.
>
> I want to start testing your patches for the EHCI stuff. Do i need
> to enable anything inorder to get EHCI emulation working after
> applying your patch?
>
> Unfortunately, with this patch it doesn't work for me. My guest host
> (windows vista) still became really slow when I add the a usb device.
> >
> > Waiting for your comments,
> > Arnon
> >
>
> Thanks,
>
> Jerry
>
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2008-03-01 21:06 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <AchjJ1AuK1ctRXY/TRuiwwGi1wuGgA==>
2008-01-30 10:03 ` [Qemu-devel] [kvm-devel] [PATCH] USB 2.0 EHCI emulation Arnon Gilboa
2008-02-28 19:46 ` [kvm-devel] [Qemu-devel] " Gerb Stralko
2008-02-29 7:33 ` Arnon Gilboa
2008-02-29 14:17 ` Gerb Stralko
2008-03-01 21:05 ` Arnon Gilboa
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).