DevOHCI.cpp revision d0e467f88aeb4288b409908dbe1b96d07c7133b2
/* $Id$ */
/** @file
* DevOHCI - Open Host Controller Interface for USB.
*/
/*
* Copyright (C) 2006-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/** @page pg_dev_ohci OHCI - Open Host Controller Interface Emulation.
*
* This component implements an OHCI USB controller. It is split roughly in
* to two main parts, the first part implements the register level
* specification of USB OHCI and the second part maintains the root hub (which
* is an integrated component of the device).
*
* The OHCI registers are used for the usual stuff like enabling and disabling
* interrupts. Since the USB time is divided in to 1ms frames and various
* interrupts may need to be triggered at frame boundary time, a timer-based
* approach was taken. Whenever the bus is enabled ohci->eof_timer will be set.
*
* The actual USB transfers are stored in main memory (along with endpoint and
* transfer descriptors). The ED's for all the control and bulk endpoints are
* found by consulting the HcControlHeadED and HcBulkHeadED registers
* respectively. Interrupt ED's are different, they are found by looking
* in the HCCA (another communication area in main memory).
*
* At the start of every frame (in function ohci_sof) we traverse all enabled
* ED lists and queue up as many transfers as possible. No attention is paid
* could conceivably contain a dozen high speed busses so this would
* artificially limit the performance.
*
* Once we have a transfer ready to go (in function ohciServiceTd) we
* allocate an URB on the stack, fill in all the relevant fields and submit
* it using the VUSBIRhSubmitUrb function. The roothub device and the virtual
* USB core code (vusb.c) coordinates everything else from this point onwards.
*
* When the URB has been successfully handed to the lower level driver, our
* prepare callback gets called and we can remove the TD from the ED transfer
* list. This stops us queueing it twice while it completes.
* bird: no, we don't remove it because that confuses the guest! (=> crashes)
*
* Completed URBs are reaped at the end of every frame (in function
* ohci_frame_boundary). Our completion routine makes use of the ED and TD
* fields in the URB to store the physical addresses of the descriptors so
* that they may be modified in the roothub callbacks. Our completion
* routine (ohciRhXferComplete) carries out a number of tasks:
* -# Retires the TD associated with the transfer, setting the
* relevant error code etc.
* -# Updates done-queue interrupt timer and potentially causes
* a writeback of the done-queue.
* -# If the transfer was device-to-host, we copy the data in to
* the host memory.
*
* As for error handling OHCI allows for 3 retries before failing a transfer,
* an error count is stored in each transfer descriptor. A halt flag is also
* stored in the transfer descriptor. That allows for ED's to be disabled
* without stopping the bus and de-queuing them.
*
* When the bus is started and stopped we call VUSBIDevPowerOn/Off() on our
* roothub to indicate it's powering up and powering down. Whenever we power
* down, the USB core makes sure to synchronously complete all outstanding
* requests so that the OHCI is never seen in an inconsistent state by the
* guest OS (Transfers are not meant to be unlinked until they've actually
* completed, but we can't do that unless we work synchronously, so we just
* have to fake it).
* bird: we do work synchronously now, anything causes guest crashes.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_DEV_USB
#include <iprt/asm-math.h>
#include <iprt/semaphore.h>
#include <iprt/critsect.h>
#ifdef IN_RING3
#endif
#include "VBoxDD.h"
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/** The saved state version. */
#define OHCI_SAVED_STATE_VERSION 4
/** The saved state version used in 3.0 and earlier.
*
* @remarks Because of the SSMR3MemPut/Get laziness we ended up with an
* accidental format change between 2.0 and 2.1 that didn't get its own
* version number. It is therefore not possible to restore states from
* 2.0 and earlier with 2.1 and later. */
#define OHCI_SAVED_STATE_VERSION_MEM_HELL 3
/** Number of Downstream Ports on the root hub.
* If you change this you need to add more status register words to the 'opreg'
* array.
*/
#define OHCI_NDP 8
/** Pointer to OHCI device data. */
/** Read-only pointer to the OHCI device data. */
/**
* An OHCI root hub port.
*/
typedef struct OHCIHUBPORT
{
/** The port register. */
#if HC_ARCH_BITS == 64
#endif
/** The device attached to the port. */
} OHCIHUBPORT;
#if HC_ARCH_BITS == 64
#endif
/** Pointer to an OHCI hub port. */
typedef OHCIHUBPORT *POHCIHUBPORT;
/**
* The OHCI root hub.
*
* @implements PDMIBASE
* @implements VUSBIROOTHUBPORT
* @implements PDMILEDPORTS
*/
typedef struct ohci_roothub
{
/** Pointer to the base interface of the VUSB RootHub. */
/** Pointer to the connector interface of the VUSB RootHub. */
/** Pointer to the device interface of the VUSB RootHub. */
/** The base interface exposed to the roothub driver. */
/** The roothub port interface exposed to the roothub driver. */
/** The LED. */
/** The LED ports. */
/** Partner of ILeds. */
#if HC_ARCH_BITS == 64
#endif
} OHCIROOTHUB;
#if HC_ARCH_BITS == 64
#endif
/** Pointer to the OHCI root hub. */
typedef OHCIROOTHUB *POHCIROOTHUB;
/**
* Data used for reattaching devices on a state load.
*/
typedef struct ohci_load {
/** Timer used once after state load to inform the guest about new devices.
* We do this to be sure the guest get any disconnect / reconnect on the
* same port. */
/** Number of detached devices. */
unsigned cDevs;
/** Array of devices which were detached. */
} OHCILOAD;
/** Pointer to an OHCILOAD structure. */
/**
* OHCI device data.
*/
typedef struct OHCI
{
/** The PCI device. */
/** Pointer to the device instance - R3 ptr. */
/** The End-Of-Frame timer - R3 Ptr. */
/** Pointer to the device instance - R0 ptr */
/** The End-Of-Frame timer - R0 Ptr. */
/** Pointer to the device instance - RC ptr. */
/** The End-Of-Frame timer - RC Ptr. */
/** Start of current frame. */
/* done queue interrupt counter */
/** frame number overflow. */
/** Address of the MMIO region assigned by PCI. */
/* Root hub device */
/* OHCI registers */
/** @name Control partition
* @{ */
/** HcControl. */
/** HcCommandStatus. */
/** HcInterruptStatus. */
/** HcInterruptEnabled. */
/** @} */
/** @name Memory pointer partition
* @{ */
/** HcHCCA. */
/** HcPeriodCurrentEd. */
/** HcControlCurrentED. */
/** HcControlHeadED. */
/** HcBlockCurrendED. */
/** HcBlockHeadED. */
/** HcDoneHead. */
/** @} */
/** @name Frame counter partition
* @{ */
/** HcFmInterval.FSMPS - FSLargestDataPacket */
/** HcFmInterval.FIT - FrameItervalToggle */
/** HcFmInterval.FI - FrameInterval */
/** HcFmRemaining.FRT - toggle bit. */
/** HcFmNumber.
* @remark The register size is 16-bit, but for debugging and performance
* reasons we maintain a 32-bit counter. */
/** HcPeriodicStart */
/** @} */
/** The number of virtual time ticks per frame. */
/** The number of virtual time ticks per USB bus tick. */
/** Number of in-flight TDs. */
unsigned cInFlight;
unsigned Alignment0; /**< Align aInFlight on a 8 byte boundary. */
/** Array of in-flight TDs. */
struct ohci_td_in_flight
{
/** Address of the transport descriptor. */
/** Flag indicating an inactive (not-linked) URB. */
bool fInactive;
/** Pointer to the URB. */
} aInFlight[257];
#if HC_ARCH_BITS == 32
#endif
/** Number of in-done-queue TDs. */
unsigned cInDoneQueue;
/** Array of in-done-queue TDs. */
struct ohci_td_in_done_queue
{
/** Address of the transport descriptor. */
} aInDoneQueue[64];
/** When the tail of the done queue was added.
* Used to calculate the age of the done queue. */
#if R3_ARCH_BITS == 32
/** Align pLoad, the stats and the struct size correctly. */
#endif
/** Pointer to state load data. */
/** Detected canceled isochronous URBs. */
/** Detected canceled general URBs. */
/** Dropped URBs (endpoint halted, or URB canceled). */
/** Profiling ohciFrameBoundaryTimer. */
/** This member and all the following are not part of saved state. */
/** VM timer frequency used for frame timer calculations. */
/** Number of USB work cycles with no transfers. */
/** Current frame timer rate (default 1000). */
/** Idle detection flag; must be cleared at start of frame */
bool fIdle;
/** A flag indicating that the bulk list may have in-flight URBs. */
bool fBulkNeedsCleaning;
bool fRZEnabled;
/** The framer thread. */
/** Event semaphore to interact with the framer thread. */
/** Flag whether the framer thread should processing frames. */
volatile bool fBusStarted;
/** Alignment. */
/** How long to wait until the next frame. */
/** Critical section to synchronize the framer and URB completion handler. */
} OHCI;
/* Standard OHCI bus speed */
#define OHCI_DEFAULT_TIMER_FREQ 1000
/* Host Controller Communications Area */
#define OHCI_HCCA_NUM_INTR 32
struct ohci_hcca
{
};
/** @name OHCI Endpoint Descriptor
* @{ */
#define ED_HWINFO_MPS 0x07ff0000
#define ED_HWINFO_ENDPOINT_SHIFT 7
#define ED_HEAD_HALTED RT_BIT(0)
/**
* OHCI Endpoint Descriptor.
*/
typedef struct OHCIED
{
/** Flags and stuff. */
/** TailP - TD Queue Tail pointer. Bits 0-3 ignored / preserved. */
/** HeadP - TD Queue head pointer. Bit 0 - Halted, Bit 1 - toggleCarry. Bit 2&3 - 0. */
/** NextED - Next Endpoint Descriptor. Bits 0-3 ignored / preserved. */
/** @} */
/** @name Completion Codes
* @{ */
/* 0x0a..0x0b - reserved */
/** @} */
/** @name OHCI General transfer descriptor
* @{ */
/** Error count (EC) shift. */
#define TD_ERRORS_SHIFT 26
/** Error count max. (One greater than what the EC field can hold.) */
#define TD_ERRORS_MAX 4
/** CC - Condition code mask. */
#define TD_HWINFO_CC_SHIFT 28
/** EC - Error count. */
/** T - Data toggle. */
/** DI - Delay interrupt. */
/** DP - Direction / PID. */
/** R - Buffer rounding. */
/** Bits that are reserved / unknown. */
/** SETUP - to endpoint. */
#define OHCI_TD_DIR_SETUP 0x0
/** OUT - to endpoint. */
#define OHCI_TD_DIR_OUT 0x1
/** IN - from endpoint. */
#define OHCI_TD_DIR_IN 0x2
/** Reserved. */
#define OHCI_TD_DIR_RESERVED 0x3
/**
* OHCI general transfer descriptor
*/
typedef struct OHCITD
{
/** CBP - Current Buffer Pointer. (32-bit physical address) */
/** NextTD - Link to the next transfer descriptor. (32-bit physical address, dword aligned) */
/** BE - Buffer End (inclusive). (32-bit physical address) */
/** @} */
/** @name OHCI isochronous transfer descriptor.
* @{ */
/** SF - Start frame number. */
#define ITD_HWINFO_SF 0xffff
/** DI - Delay interrupt. (TD_HWINFO_DI) */
#define ITD_HWINFO_DI_SHIFT 21
/** FC - Frame count. */
#define ITD_HWINFO_FC_SHIFT 24
/** CC - Condition code mask. (=TD_HWINFO_CC) */
#define ITD_HWINFO_CC_SHIFT 28
/** The buffer page 0 mask (lower 12 bits are ignored). */
#define ITD_NUM_PSW 8
/** OFFSET - offset of the package into the buffer page.
* (Only valid when CC set to Not Accessed.)
*
* Note that the top bit of the OFFSET field is overlapping with the
* first bit in the CC field. This is ok because both 0xf and 0xe are
* defined as "Not Accessed".
*/
#define ITD_PSW_OFFSET 0x1fff
/** SIZE field mask for IN bound transfers.
* (Only valid when CC isn't Not Accessed.)*/
#define ITD_PSW_SIZE 0x07ff
/** CC field mask.
* USed to indicate the format of SIZE (Not Accessed -> OFFSET). */
#define ITD_PSW_CC 0xf000
#define ITD_PSW_CC_SHIFT 12
/**
* OHCI isochronous transfer descriptor.
*/
typedef struct OHCIITD
{
/** BP0 - Buffer Page 0. The lower 12 bits are ignored. */
/** NextTD - Link to the next transfer descriptor. (32-bit physical address, dword aligned) */
/** BE - Buffer End (inclusive). (32-bit physical address) */
/** (OffsetN/)PSWN - package status word array (0..7).
* The format varies depending on whether the package has been completed or not. */
/** @} */
/**
* OHCI register operator.
*/
typedef struct ohci_opreg
{
const char *pszName;
} OHCIOPREG;
/* OHCI Local stuff */
#define OHCI_USB_RESET 0x00
#define OHCI_USB_RESUME 0x40
#define OHCI_USB_OPERATIONAL 0x80
#define OHCI_USB_SUSPEND 0xc0
#define OHCI_STATUS_HCR (1<<0)
* @{ */
/** SO - Scheduling overrun. */
#define OHCI_INTR_SCHEDULEING_OVERRUN RT_BIT(0)
/** WDH - HcDoneHead writeback. */
/** SF - Start of frame. */
/** RD - Resume detect. */
/** UE - Unrecoverable error. */
/** FNO - Frame number overflow. */
/** RHSC- Root hub status change. */
/** OC - Ownership change. */
/** MIE - Master interrupt enable. */
/** @} */
#define OHCI_HCCA_SIZE 0x100
#define OHCI_FMI_FSMPS_SHIFT 16
#define OHCI_FMI_FIT_SHIFT 31
#define OHCI_LS_THRESH 0x628
#define OHCI_RHA_NDP (0xff)
#define OHCI_RHS_LPS RT_BIT_32(0)
/** @name HcRhPortStatus[n] - RH Port Status register (read).
* @{ */
/** CCS - CurrentConnectionStatus - 0 = no device, 1 = device. */
#define OHCI_PORT_CCS RT_BIT(0)
/** PES - PortEnableStatus. */
/** PSS - PortSuspendStatus */
/** POCI- PortOverCurrentIndicator. */
/** PRS - PortResetStatus */
/** PPS - PortPowerStatus */
/** LSDA - LowSpeedDeviceAttached */
/** CSC - ConnectStatusChange */
/** PESC - PortEnableStatusChange */
/** PSSC - PortSuspendStatusChange */
/** OCIC - OverCurrentIndicatorChange */
/** PRSC - PortResetStatusChange */
/** @} */
/** @name HcRhPortStatus[n] - Root Hub Port Status Registers - read.
* @{ */
/** CCS - CurrentConnectStatus - 0 = no device, 1 = device. */
#define OHCI_PORT_R_CURRENT_CONNECT_STATUS RT_BIT(0)
/** PES - PortEnableStatus. */
/** PSS - PortSuspendStatus */
/** POCI- PortOverCurrentIndicator. */
/** PRS - PortResetStatus */
/** PPS - PortPowerStatus */
/** LSDA - LowSpeedDeviceAttached */
/** CSC - ConnectStatusChange */
/** PESC - PortEnableStatusChange */
/** PSSC - PortSuspendStatusChange */
/** OCIC - OverCurrentIndicatorChange */
/** PRSC - PortResetStatusChange */
/** @} */
/** @name HcRhPortStatus[n] - Root Hub Port Status Registers - write.
* @{ */
/** CCS - ClearPortEnable. */
#define OHCI_PORT_W_CLEAR_ENABLE RT_BIT(0)
/** PES - SetPortEnable. */
/** PSS - SetPortSuspend */
/** POCI- ClearSuspendStatus. */
/** PRS - SetPortReset */
/** PPS - SetPortPower */
/** LSDA - ClearPortPower */
/** CSC - ClearConnectStatusChange */
/** PESC - PortEnableStatusChange */
/** PSSC - PortSuspendStatusChange */
/** OCIC - OverCurrentIndicatorChange */
/** PRSC - PortResetStatusChange */
/** The mask of bit which are used to clear themselves. */
#define OHCI_PORT_W_CLEAR_CHANGE_MASK ( OHCI_PORT_W_CLEAR_CSC | OHCI_PORT_W_CLEAR_PESC | OHCI_PORT_W_CLEAR_PSSC \
/** @} */
#ifndef VBOX_DEVICE_STRUCT_TESTCASE
/*******************************************************************************
* Global Variables *
*******************************************************************************/
#if defined(LOG_ENABLED) && defined(IN_RING3)
static bool g_fLogBulkEPs = false;
static bool g_fLogControlEPs = false;
static bool g_fLogInterruptEPs = false;
#endif
#ifdef IN_RING3
/**
* SSM descriptor table for the OHCI structure.
*/
static SSMFIELD const g_aOhciFields[] =
{
};
#endif
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
#ifdef IN_RING3
/* Update host controller state to reflect a device attach */
# if defined(VBOX_STRICT) || defined(LOG_ENABLED)
# endif
static DECLCALLBACK(void) ohciR3LoadReattachDevices(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser);
#endif /* IN_RING3 */
/**
* Update PCI IRQ levels
*/
{
int level = 0;
#ifdef IN_RING3
#endif
level = 1;
if (level)
{
Log2(("ohci: Fired off interrupt %#010x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d - %s\n",
}
#ifdef IN_RING3
#endif
}
/**
* Set an interrupt, use the wrapper ohciSetInterrupt.
*/
{
return;
}
/**
* Set an interrupt wrapper macro for logging purposes.
*/
#ifdef IN_RING3
/* Carry out a hardware remote wakeup */
{
return;
return;
}
/**
* Query interface method for the roothub LUN.
*/
{
return NULL;
}
/**
* Gets the pointer to the status LED of a unit.
*
* @returns VBox status code.
* @param pInterface Pointer to the interface structure containing the called function pointer.
* @param iLUN The unit which status LED we desire.
* @param ppLed Where to store the LED pointer.
*/
static DECLCALLBACK(int) ohciRhQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
{
if (iLUN == 0)
{
return VINF_SUCCESS;
}
return VERR_PDM_LUN_NOT_FOUND;
}
/** Converts a OHCI.roothub.IRhPort pointer to a POHCI. */
#define VUSBIROOTHUBPORT_2_OHCI(pInterface) ((POHCI)( (uintptr_t)(pInterface) - RT_OFFSETOF(OHCI, RootHub.IRhPort) ))
/**
* Get the number of available ports in the hub.
*
* @returns The number of ports available.
* @param pInterface Pointer to this structure.
* @param pAvailable Bitmap indicating the available ports. Set bit == available port.
*/
static DECLCALLBACK(unsigned) ohciRhGetAvailablePorts(PVUSBIROOTHUBPORT pInterface, PVUSBPORTBITMAP pAvailable)
{
unsigned iPort;
unsigned cPorts = 0;
{
{
cPorts++;
}
}
return cPorts;
}
/**
* Gets the supported USB versions.
*
* @returns The mask of supported USB versions.
* @param pInterface Pointer to this structure.
*/
{
return VUSB_STDVER_11;
}
/**
* A device is being attached to a port in the roothub.
*
* @param pInterface Pointer to this structure.
* @param pDev Pointer to the device being attached.
* @param uPort The port number assigned to the device.
*/
static DECLCALLBACK(int) ohciRhAttach(PVUSBIROOTHUBPORT pInterface, PVUSBIDEVICE pDev, unsigned uPort)
{
/*
* Validate and adjust input.
*/
uPort--;
/*
* Attach it.
*/
pThis->RootHub.aPorts[uPort].fReg = OHCI_PORT_R_CURRENT_CONNECT_STATUS | OHCI_PORT_R_CONNECT_STATUS_CHANGE;
return VINF_SUCCESS;
}
/**
* A device is being detached from a port in the roothub.
*
* @param pInterface Pointer to this structure.
* @param pDev Pointer to the device being detached.
* @param uPort The port number assigned to the device.
*/
static DECLCALLBACK(void) ohciRhDetach(PVUSBIROOTHUBPORT pInterface, PVUSBIDEVICE pDev, unsigned uPort)
{
/*
* Validate and adjust input.
*/
uPort--;
/*
* Detach it.
*/
else
}
#ifdef IN_RING3
/**
* One of the roothub devices has completed its reset operation.
*
* Currently, we don't think anything is required to be done here
* so it's just a stub for forcing async resetting of the devices
* during a root hub reset.
*
* @param pDev The root hub device.
* @param rc The result of the operation.
* @param pvUser Pointer to the controller.
*/
{
}
#endif
/**
* Reset the root hub.
*
* @returns VBox status code.
* @param pInterface Pointer to this structure.
* @param fResetOnLinux This is used to indicate whether we're at VM reset time and
* can do real resets or if we're at any other time where that
* isn't such a good idea.
* @remark Do NOT call VUSBIDevReset on the root hub in an async fashion!
* @thread EMT
*/
{
/*
* We're pending to _reattach_ the device without resetting them.
* Except, during VM reset where we use the opportunity to do a proper
* reset before the guest comes along and expect things.
*
* However, it's very very likely that we're not doing the right thing
* here if coming from the guest (USB Reset state). The docs talks about
* root hub resetting, however what exact behaviour in terms of root hub
* status and changed bits, and HC interrupts aren't stated clearly. IF we
* get trouble and see the guest doing "USB Resets" we will have to look
* into this. For the time being we stick with simple.
*/
{
{
pThis->RootHub.aPorts[iPort].fReg = OHCI_PORT_R_CURRENT_CONNECT_STATUS | OHCI_PORT_R_CONNECT_STATUS_CHANGE;
if (fResetOnLinux)
{
}
}
else
}
return VINF_SUCCESS;
}
/**
* Does a software or hardware reset of the controller.
*
* This is called in response to setting HcCommandStatus.HCR, hardware reset,
* and device construction.
*
* @param pThis The ohci instance data.
* @param fNewMode The new mode of operation. This is UsbSuspend if it's a
* software reset, and UsbReset if it's a hardware reset / cold boot.
* @param fResetOnLinux Set if we can do a real reset of the devices attached to the root hub.
* This is really a just a hack for the non-working linux device reset.
* Linux has this feature called 'logical disconnect' if device reset fails
* which prevents us from doing resets when the guest asks for it - the guest
* will get confused when the device seems to be reconnected everytime it tries
* to reset it. But if we're at hardware reset time, we can allow a device to
* be 'reconnected' without upsetting the guest.
*
* @remark This hasn't got anything to do with software setting the mode to UsbReset.
*/
{
/*
* Cancel all outstanding URBs.
*
* We can't, and won't, deal with URBs until we're moved out of the
* any more when a reset has been signaled.
*/
/*
* Reset the hardware registers.
*/
if (fNewMode == OHCI_USB_RESET)
else
pThis->intr_status = 0;
pThis->intr = OHCI_INTR_MASTER_INTERRUPT_ENABLED; /* (We follow the text and the not reset value column,) */
pThis->HcFmNumber = 0;
/*
* If this is a hardware reset, we will initialize the root hub too.
* Software resets doesn't do this according to the specs.
* (It's not possible to have device connected at the time of the
* device construction, so nothing to worry about there.)
*/
if (fNewMode == OHCI_USB_RESET)
}
#endif /* IN_RING3 */
/**
* Reads physical memory.
*/
{
if (cbBuf)
}
/**
* Writes physical memory.
*/
{
if (cbBuf)
}
/**
* Read an array of dwords from physical memory and correct endianness.
*/
{
#if BYTE_ORDER != LITTLE_ENDIAN
for(int i = 0; i < c32s; i++)
#endif
}
/**
* Write an array of dwords from physical memory and correct endianness.
*/
{
#if BYTE_ORDER == LITTLE_ENDIAN
#else
{
}
#endif
}
#ifdef IN_RING3
/**
* Reads an OHCIED.
*/
{
}
/**
* Reads an OHCITD.
*/
{
#ifdef LOG_ENABLED
if (LogIs3Enabled())
{
Log3(("ohciReadTd(,%#010x,): R=%d DP=%d DI=%d T=%d EC=%d CC=%#x CBP=%#010x NextTD=%#010x BE=%#010x UNK=%#x\n",
#if 0
if (LogIs3Enabled())
{
/*
* usbohci.sys (32-bit XP) allocates 0x80 bytes per TD:
* 0x00-0x0f is the OHCI TD.
* 0x10-0x1f for isochronous TDs
* 0x20 is the physical address of this TD.
* 0x24 is initialized with 0x64745948, probably a magic.
* 0x28 is some kind of flags. the first bit begin the allocated / not allocated indicator.
* 0x30 is a pointer to something. endpoint? interface? device?
* 0x38 is initialized to 0xdeadface. but is changed into a pointer or something.
* 0x40 looks like a pointer.
* The rest is unknown and initialized with zeros.
*/
Log3(("WinXpTd: alloc=%d PhysSelf=%RX32 s2=%RX32 magic=%RX32 s4=%RX32 s5=%RX32\n"
"%.*Rhxd\n",
}
#endif
}
#endif
}
/**
* Reads an OHCIITD.
*/
{
#ifdef LOG_ENABLED
if (LogIs3Enabled())
{
Log3(("ohciReadITd(,%#010x,): SF=%#06x (%#RX32) DI=%#x FC=%d CC=%#x BP0=%#010x NextTD=%#010x BE=%#010x\n",
Log3(("psw0=%x:%03x psw1=%x:%03x psw2=%x:%03x psw3=%x:%03x psw4=%x:%03x psw5=%x:%03x psw6=%x:%03x psw7=%x:%03x\n",
}
#endif
}
/**
* Writes an OHCIED.
*/
{
#ifdef LOG_ENABLED
if (LogIs3Enabled())
{
Log3(("ohciWriteEd(,%#010x,): %sFA=%#x %sEN=%#x %sD=%#x %sS=%d %sK=%d %sF=%d %sMPS=%#x %sTailP=%#010x %sHeadP=%#010x %sH=%d %sC=%d %sNextED=%#010x\n",
}
#endif
}
/**
* Writes an OHCITD.
*/
{
#ifdef LOG_ENABLED
if (LogIs3Enabled())
{
Log3(("ohciWriteTd(,%#010x,): %sR=%d %sDP=%d %sDI=%#x %sT=%d %sEC=%d %sCC=%#x %sCBP=%#010x %sNextTD=%#010x %sBE=%#010x (%s)\n",
pszLogMsg));
}
#endif
}
/**
* Writes an OHCIITD.
*/
{
#ifdef LOG_ENABLED
if (LogIs3Enabled())
{
Log3(("ohciWriteITd(,%#010x,): %sSF=%#x (now=%#RX32) %sDI=%#x %sFC=%d %sCC=%#x %sBP0=%#010x %sNextTD=%#010x %sBE=%#010x (%s)\n",
pszLogMsg));
Log3(("psw0=%s%x:%s%03x psw1=%s%x:%s%03x psw2=%s%x:%s%03x psw3=%s%x:%s%03x psw4=%s%x:%s%03x psw5=%s%x:%s%03x psw6=%s%x:%s%03x psw7=%s%x:%s%03x\n",
(ITdOld.aPSW[0] >> 12) != (pITd->aPSW[0] >> 12) ? "*" : "", pITd->aPSW[0] >> 12, (ITdOld.aPSW[0] & 0xfff) != (pITd->aPSW[0] & 0xfff) ? "*" : "", pITd->aPSW[0] & 0xfff,
(ITdOld.aPSW[1] >> 12) != (pITd->aPSW[1] >> 12) ? "*" : "", pITd->aPSW[1] >> 12, (ITdOld.aPSW[1] & 0xfff) != (pITd->aPSW[1] & 0xfff) ? "*" : "", pITd->aPSW[1] & 0xfff,
(ITdOld.aPSW[2] >> 12) != (pITd->aPSW[2] >> 12) ? "*" : "", pITd->aPSW[2] >> 12, (ITdOld.aPSW[2] & 0xfff) != (pITd->aPSW[2] & 0xfff) ? "*" : "", pITd->aPSW[2] & 0xfff,
(ITdOld.aPSW[3] >> 12) != (pITd->aPSW[3] >> 12) ? "*" : "", pITd->aPSW[3] >> 12, (ITdOld.aPSW[3] & 0xfff) != (pITd->aPSW[3] & 0xfff) ? "*" : "", pITd->aPSW[3] & 0xfff,
(ITdOld.aPSW[4] >> 12) != (pITd->aPSW[4] >> 12) ? "*" : "", pITd->aPSW[4] >> 12, (ITdOld.aPSW[4] & 0xfff) != (pITd->aPSW[4] & 0xfff) ? "*" : "", pITd->aPSW[4] & 0xfff,
(ITdOld.aPSW[5] >> 12) != (pITd->aPSW[5] >> 12) ? "*" : "", pITd->aPSW[5] >> 12, (ITdOld.aPSW[5] & 0xfff) != (pITd->aPSW[5] & 0xfff) ? "*" : "", pITd->aPSW[5] & 0xfff,
(ITdOld.aPSW[6] >> 12) != (pITd->aPSW[6] >> 12) ? "*" : "", pITd->aPSW[6] >> 12, (ITdOld.aPSW[6] & 0xfff) != (pITd->aPSW[6] & 0xfff) ? "*" : "", pITd->aPSW[6] & 0xfff,
(ITdOld.aPSW[7] >> 12) != (pITd->aPSW[7] >> 12) ? "*" : "", pITd->aPSW[7] >> 12, (ITdOld.aPSW[7] & 0xfff) != (pITd->aPSW[7] & 0xfff) ? "*" : "", pITd->aPSW[7] & 0xfff));
}
#endif
}
#ifdef LOG_ENABLED
/**
* Core TD queue dumper. LOG_ENABLED builds only.
*/
DECLINLINE(void) ohciDumpTdQueueCore(POHCI pThis, uint32_t GCPhysHead, uint32_t GCPhysTail, bool fFull)
{
int cMax = 100;
for (;;)
{
break;
/* can't use ohciReadTd() because of Log4. */
if (fFull)
Log4((" [R=%d DP=%d DI=%d T=%d EC=%d CC=%#x CBP=%#010x NextTD=%#010x BE=%#010x] -> ",
else
Log4((" -> "));
}
}
/**
* Dumps a TD queue. LOG_ENABLED builds only.
*/
{
if (pszMsg)
Log4(("\n"));
}
/**
* Core ITD queue dumper. LOG_ENABLED builds only.
*/
DECLINLINE(void) ohciDumpITdQueueCore(POHCI pThis, uint32_t GCPhysHead, uint32_t GCPhysTail, bool fFull)
{
int cMax = 100;
for (;;)
{
break;
/* can't use ohciReadTd() because of Log4. */
/*if (fFull)
Log4((" [R=%d DP=%d DI=%d T=%d EC=%d CC=%#x CBP=%#010x NextTD=%#010x BE=%#010x] -> ",
(Td.hwinfo >> 18) & 1,
(Td.hwinfo >> 19) & 3,
(Td.hwinfo >> 21) & 7,
(Td.hwinfo >> 24) & 3,
(Td.hwinfo >> 26) & 3,
(Td.hwinfo >> 28) &15,
Td.cbp,
Td.NextTD,
Td.be));
else*/
Log4((" -> "));
}
}
/**
* Dumps a ED list. LOG_ENABLED builds only.
*/
{
if (pszMsg)
for (;;)
{
/* ED */
if (!GCPhys)
{
Log4(("END}\n"));
return;
}
/* TDs */
Log4(("[I]"));
{
Log4(("SH}"));
Log4(("S-}"));
else
Log4(("-H}"));
}
else
{
else
Log4(("}"));
}
/* next */
}
Log4(("\n"));
}
#endif /* LOG_ENABLED */
{
unsigned i = iStart;
{
return i;
i++;
}
i = iStart;
while (i-- > 0)
{
return i;
}
return -1;
}
/**
* Record an in-flight TD.
*
* @param pThis OHCI instance data.
* @param GCPhysTD Physical address of the TD.
* @param pUrb The URB.
*/
{
if (i >= 0)
{
#ifdef LOG_ENABLED
#endif
return;
}
}
/**
* Record in-flight TDs for an URB.
*
* @param pThis OHCI instance data.
* @param pUrb The URB.
*/
{
}
/**
* Finds a in-flight TD.
*
* @returns Index of the record.
* @returns -1 if not found.
* @param pThis OHCI instance data.
* @param GCPhysTD Physical address of the TD.
* @remark This has to be fast.
*/
{
const int iLast = i;
{
return i;
if (cLeft-- <= 1)
return -1;
i++;
}
i = iLast;
while (i-- > 0)
{
return i;
if (cLeft-- <= 1)
return -1;
}
return -1;
}
/**
* Checks if a TD is in-flight.
*
* @returns true if in flight, false if not.
* @param pThis OHCI instance data.
* @param GCPhysTD Physical address of the TD.
*/
{
}
/**
* Returns a URB associated with an in-flight TD, if any.
*
* @returns pointer to URB if TD is in flight.
* @returns NULL if not in flight.
* @param pThis OHCI instance data.
* @param GCPhysTD Physical address of the TD.
*/
{
int i;
if ( i >= 0 )
return NULL;
}
/**
* Removes a in-flight TD.
*
* @returns 0 if found. For logged builds this is the number of frames the TD has been in-flight.
* @returns -1 if not found.
* @param pThis OHCI instance data.
* @param GCPhysTD Physical address of the TD.
*/
{
if (i >= 0)
{
#ifdef LOG_ENABLED
#else
const int cFramesInFlight = 0;
#endif
Log2(("ohci_in_flight_remove: reaping TD=%#010x %d frames (%#010x-%#010x)\n",
return cFramesInFlight;
}
return -1;
}
/**
* Removes all TDs associated with a URB from the in-flight tracking.
*
* @returns 0 if found. For logged builds this is the number of frames the TD has been in-flight.
* @returns -1 if not found.
* @param pThis OHCI instance data.
* @param pUrb The URB.
*/
{
{
cFramesInFlight = -1;
}
return cFramesInFlight;
}
#if defined(VBOX_STRICT) || defined(LOG_ENABLED)
/**
* Empties the in-done-queue.
* @param pThis OHCI instance data.
*/
{
pThis->cInDoneQueue = 0;
}
/**
* Finds a TD in the in-done-queue.
* @returns >= 0 on success.
* @returns -1 if not found.
* @param pThis OHCI instance data.
* @param GCPhysTD Physical address of the TD.
*/
{
unsigned i = pThis->cInDoneQueue;
while (i-- > 0)
return i;
return -1;
}
/**
* Checks that the specified TD is not in the done queue.
* @param pThis OHCI instance data.
* @param GCPhysTD Physical address of the TD.
*/
{
#if 0
/* This condition has been observed with the USB tablet emulation or with
* a real USB mouse and an SMP XP guest. I am also not sure if this is
* really a problem for us. The assertion checks that the guest doesn't
* re-submit a TD which is still in the done queue. It seems to me that
* this should only be a problem if we either keep track of TDs in the done
* queue somewhere else as well (in which case we should also free those
* references in time, and I can't see any code doing that) or if we
* manipulate TDs in the done queue in some way that might fail if they are
* re-submitted (can't see anything like that either).
*/
#endif
return i < 0;
}
# ifdef VBOX_STRICT
/**
* Adds a TD to the in-done-queue tracking, checking that it's not there already.
* @param pThis OHCI instance data.
* @param GCPhysTD Physical address of the TD.
*/
{
}
# endif /* VBOX_STRICT */
#endif /* defined(VBOX_STRICT) || defined(LOG_ENABLED) */
/**
* OHCI Transport Buffer - represents a OHCI Transport Descriptor (TD).
* A TD may be split over max 2 pages.
*/
typedef struct OHCIBUF
{
/** Pages involved. */
struct OHCIBUFVEC
{
/** The 32-bit physical address of this part. */
/** The length. */
} aVecs[2];
/** Number of valid entries in aVecs. */
/** The total length. */
/**
* Sets up a OHCI transport buffer.
*
* @param pBuf Ohci buffer.
* @param cbp Current buffer pointer. 32-bit physical address.
* @param be Last byte in buffer (BufferEnd). 32-bit physical address.
*/
{
{
}
{
}
else
{
}
}
/**
* Updates a OHCI transport buffer.
*
* This is called upon completion to adjust the sector lengths if
* the total length has changed. (received less then we had space for
* or a partial transfer.)
*
* @param pBuf The buffer to update. cbTotal contains the new total on input.
* While the aVecs[*].cb members is updated upon return.
*/
{
{
{
return;
}
}
}
/** A worker for ohciUnlinkTds(). */
{
Log(("ohciUnlinkIsocTdInList: Unlinking non-head ITD! TdAddr=%#010RX32 HeadTdAddr=%#010RX32 LastEdAddr=%#010RX32\n",
while ( CurTdAddr != LastTdAddr
&& cMax-- > 0)
{
{
return true;
}
/* next */
}
Log(("ohciUnlinkIsocTdInList: TdAddr=%#010RX32 wasn't found in the list!!! (cMax=%d)\n", TdAddr, cMax));
return false;
}
/** A worker for ohciUnlinkTds(). */
{
Log(("ohciUnlinkGeneralTdInList: Unlinking non-head TD! TdAddr=%#010RX32 HeadTdAddr=%#010RX32 LastEdAddr=%#010RX32\n",
while ( CurTdAddr != LastTdAddr
&& cMax-- > 0)
{
{
return true;
}
/* next */
}
Log(("ohciUnlinkGeneralTdInList: TdAddr=%#010RX32 wasn't found in the list!!! (cMax=%d)\n", TdAddr, cMax));
return false;
}
/**
* Unlinks the TDs that makes up the URB from the ED.
*
* @returns success indicator. true if successfully unlinked.
* @returns false if the TD was not found in the list.
*/
{
/*
* Don't unlink more than once.
*/
return true;
{
{
/*
* Unlink the TD from the ED list.
* The normal case is that it's at the head of the list.
*/
{
}
else
{
/*
* It's probably somewhere in the list, not a unlikely situation with
* the current isochronous code.
*/
return false;
}
}
}
else
{
{
/** @todo r=bird: Messing with the toggle flag in prepare is probably not correct
* when we encounter a STALL error, 4.3.1.3.7.2: "If an endpoint returns a STALL
* PID, the Host Controller retires the General TD with the ConditionCode set
* to STALL and halts the endpoint. The CurrentBufferPointer, ErrorCount, and
* dataToggle fields retain the values that they had at the start of the
* transaction." */
/* update toggle and set data toggle carry */
{
if ( !!(pTd->hwinfo & TD_HWINFO_TOGGLE_LO) ) /** @todo r=bird: is it just me or doesn't this make sense at all? */
else
}
else
{
if ( !!(pEd->HeadP & ED_HEAD_CARRY) ) /** @todo r=bird: is it just me or doesn't this make sense at all? */
else
}
/*
* Unlink the TD from the ED list.
* The normal case is that it's at the head of the list.
*/
{
}
else
{
/*
* The TD is probably somewhere in the list.
*
* This shouldn't ever happen unless there was a failure! Even on failure,
* we can screw up the HCD state by picking out a TD from within the list
* like this! If this turns out to be a problem, we have to find a better
* solution. For now we'll hope the HCD handles it...
*/
return false;
}
/*
* Only unlink the first TD on error.
* See comment in ohciRhXferCompleteGeneralURB().
*/
break;
}
}
return true;
}
/**
* Checks that the transport descriptors associated with the URB
* hasn't been changed in any way indicating that they may have been canceled.
*
* This rountine also updates the TD copies contained within the URB.
*
* @returns true if the URB has been canceled, otherwise false.
* @param pThis The OHCI instance.
* @param pUrb The URB in question.
* @param pEd The ED pointer (optional).
*/
{
if (!pUrb)
return true;
/*
* Make sure we've got an endpoint descriptor so we can
* check for tail TDs.
*/
if (!pEd)
{
}
{
{
union
{
} u;
{
Log(("%s: ohciHasUrbBeenCanceled: iTd=%d cTds=%d TdAddr=%#010RX32 canceled (tail)! [iso]\n",
return true;
}
)
{
Log(("%s: ohciHasUrbBeenCanceled: iTd=%d cTds=%d TdAddr=%#010RX32 canceled! [iso]\n",
Log2((" %.*Rhxs (cur)\n"
"!= %.*Rhxs (copy)\n",
return true;
}
}
}
else
{
{
union
{
} u;
{
Log(("%s: ohciHasUrbBeenCanceled: iTd=%d cTds=%d TdAddr=%#010RX32 canceled (tail)!\n",
return true;
}
)
{
Log(("%s: ohciHasUrbBeenCanceled: iTd=%d cTds=%d TdAddr=%#010RX32 canceled!\n",
Log2((" %.*Rhxs (cur)\n"
"!= %.*Rhxs (copy)\n",
return true;
}
}
}
return false;
}
/**
* Returns the OHCI_CC_* corresponding to the VUSB status code.
*
* @returns OHCI_CC_* value.
* @param enmStatus The VUSB status code.
*/
{
switch (enmStatus)
{
case VUSBSTATUS_OK: return OHCI_CC_NO_ERROR;
case VUSBSTATUS_STALL: return OHCI_CC_STALL;
case VUSBSTATUS_CRC: return OHCI_CC_CRC;
case VUSBSTATUS_DATA_UNDERRUN: return OHCI_CC_DATA_UNDERRUN;
case VUSBSTATUS_DATA_OVERRUN: return OHCI_CC_DATA_OVERRUN;
case VUSBSTATUS_DNR: return OHCI_CC_DNR;
case VUSBSTATUS_NOT_ACCESSED: return OHCI_CC_NOT_ACCESSED_1;
default:
return OHCI_CC_DNR;
}
}
/**
* Worker for ohciRhXferCompletion that handles the completion of
* a URB made up of isochronous TDs.
*
* In general, all URBs should have status OK.
*/
{
/*
* Copy the data back (if IN operation) and update the TDs.
*/
{
if (R >= 8)
R = 0; /* submitted ahead of time. */
/*
* Only one case of TD level condition code is document, so
* just set NO_ERROR here to reduce number duplicate code.
*/
AssertCompile(OHCI_CC_NO_ERROR == 0);
{
/*
* Update the frames and copy back the data.
* We assume that we don't get incorrect lengths here.
*/
for (unsigned i = 0; i < cFrames; i++)
{
if ( i < R
{
/* It should already be NotAccessed. */
continue;
}
/* Update the PSW (save the offset first in case of a IN). */
>> (TD_HWINFO_CC_SHIFT - ITD_PSW_CC_SHIFT);
{
/* Set the size. */
/* Copy data. */
if (cb)
{
{
if (off < 0x1000)
{
/* both */
}
else /* only in the 2nd page */
}
else /* only in the 1st page */
Log5(("packet %d: off=%#x cb=%#x pb=%p (%#x)\n"
"%.*Rhxd\n",
//off += cb;
}
}
}
/*
* If the last package ended with a NotAccessed status, set ITD CC
* to DataOverrun to indicate scheduling overrun.
*/
}
else
{
/*
* Most status codes only applies to the individual packets.
*
* If we get a URB level error code of this kind, we'll distribute
* it to all the packages unless some other status is available for
* a package. This is a bit fuzzy, and we will get rid of this code
* before long!
*/
//if (pUrb->enmStatus != VUSBSTATUS_DATA_OVERRUN)
{
>> (TD_HWINFO_CC_SHIFT - ITD_PSW_CC_SHIFT);
for (unsigned i = 0; i < cFrames; i++)
}
//else
// pITd->HwInfo |= ohciVUsbStatus2OhciStatus(pUrb->enmStatus);
}
/*
* Update the done queue interrupt timer.
*/
DoneInt = 0; /* It's cleared on error. */
if ( DoneInt != 0x7
/*
* Move on to the done list and write back the modified TD.
*/
#ifdef LOG_ENABLED
# ifdef VBOX_STRICT
# endif
#endif
Log(("%s: ohciRhXferCompleteIsochronousURB: ITdAddr=%#010x EdAddr=%#010x SF=%#x (%#x) CC=%#x FC=%d "
"psw0=%x:%x psw1=%x:%x psw2=%x:%x psw3=%x:%x psw4=%x:%x psw5=%x:%x psw6=%x:%x psw7=%x:%x R=%d\n",
R));
}
}
/**
* Worker for ohciRhXferCompletion that handles the completion of
* a URB made up of general TDs.
*/
{
/*
* Copy the data back (if IN operation) and update the TDs.
*/
{
/*
* Setup a ohci transfer buffer and calc the new cbp value.
*/
NewCbp = 0;
else
{
/* (len may have changed for short transfers) */
ohciBufUpdate(&Buf);
}
/*
* Write back IN buffers.
*/
{
}
/* advance the data buffer. */
/*
* Set writeback field.
*/
/* zero out writeback fields for retirement */
{
/* update done queue interrupt timer */
if ( DoneInt != 0x7
Log(("%s: ohciRhXferCompleteGeneralURB: ED=%#010x TD=%#010x Age=%d cbTotal=%#x NewCbp=%#010RX32 dqic=%d\n",
pUrb->pszDesc, pUrb->Hci.EdAddr, TdAddr, cFmAge, pUrb->enmStatus, Buf.cbTotal, NewCbp, pThis->dqic));
}
else
{
Log(("%s: ohciRhXferCompleteGeneralURB: HALTED ED=%#010x TD=%#010x (age %d) pUrb->enmStatus=%d\n",
* then the Done Queue Interrupt Counter is cleared as if the
* InterruptDelay field were zero."
*/
{
case VUSBSTATUS_STALL:
break;
case VUSBSTATUS_CRC:
break;
case VUSBSTATUS_DATA_UNDERRUN:
break;
case VUSBSTATUS_DATA_OVERRUN:
break;
default: /* what the hell */
case VUSBSTATUS_DNR:
break;
}
}
/*
* Move on to the done list and write back the modified TD.
*/
#ifdef LOG_ENABLED
# ifdef VBOX_STRICT
# endif
#endif
/*
* If we've halted the endpoint, we stop here.
* ohciUnlinkTds() will make sure we've only unliked the first TD.
*
* The reason for this is that while we can have more than one TD in a URB, real
* OHCI hardware will only deal with one TD at the time and it's therefore incorrect
* to retire TDs after the endpoint has been halted. Win2k will crash or enter infinite
* kernel loop if we don't behave correctly. (See @bugref{1646}.)
*/
break;
}
}
/**
* Transfer completion callback routine.
*
* VUSB will call this when a transfer have been completed
* in a one or another way.
*
* @param pInterface Pointer to OHCI::ROOTHUB::IRhPort.
* @param pUrb Pointer to the URB in question.
*/
{
LogFlow(("%s: ohciRhXferCompletion: EdAddr=%#010RX32 cTds=%d TdAddr0=%#010RX32\n",
/* get the current end point descriptor. */
/*
* Check that the URB hasn't been canceled and then try unlink the TDs.
*
* means the HCD has canceled the URB.
*
* If we succeed here (i.e. not dropping the URB), the TdCopy members will
* be updated but not yet written. We will delay the writing till we're done
* with the data copying, buffer pointer advancing and error handling.
*/
{
/* Leave the TD alone - the HCD doesn't want us talking to the device. */
Log(("%s: ohciRhXferCompletion: CANCELED {ED=%#010x cTds=%d TD0=%#010x age %d}\n",
return;
}
bool fHasBeenCanceled = false;
|| cFmAge < 0
)
{
Log(("%s: ohciRhXferCompletion: DROPPED {ED=%#010x cTds=%d TD0=%#010x age %d} because:%s%s%s%s%s!!!\n",
return;
}
/*
* Complete the TD updating and write the back.
* When appropriate also copy data back to the guest memory.
*/
else
/* finally write back the endpoint descriptor. */
}
/**
* Handle transfer errors.
*
* VUSB calls this when a transfer attempt failed. This function will respond
* indicating whether to retry or complete the URB with failure.
*
* @returns true if the URB should be retired.
* @returns false if the URB should be retried.
* @param pInterface Pointer to OHCI::ROOTHUB::IRhPort.
* @param pUrb Pointer to the URB in question.
*/
{
/*
* Isochronous URBs can't be retried.
*/
return true;
/*
* Don't retry on stall.
*/
{
return true;
}
bool fRetire = false;
/*
* Check if the TDs still are valid.
* This will make sure the TdCopy is up to date.
*/
/** @todo IMPORTANT! we must check if the ED is still valid at this point!!! */
{
fRetire = true;
}
else
{
/*
* Get and update the error counter.
*/
cErrs++;
{
fRetire = true;
}
else
}
return fRetire;
}
/**
* Service a general transport descriptor.
*/
static bool ohciServiceTd(POHCI pThis, VUSBXFERTYPE enmType, PCOHCIED pEd, uint32_t EdAddr, uint32_t TdAddr, uint32_t *pNextTdAddr, const char *pszListName)
{
/*
* Read the TD and setup the buffer data.
*/
/*
* Determine the direction.
*/
{
default:
{
case 0: enmDir = VUSBDIRECTION_SETUP; break;
default:
Log(("ohciServiceTd: Invalid direction!!!! Td.hwinfo=%#x Ed.hwdinfo=%#x\n", Td.hwinfo, pEd->hwinfo));
/* TODO: Do the correct thing here */
return false;
}
break;
}
/*
* Allocate and initialize a new URB.
*/
PVUSBURB pUrb = VUSBIRhNewUrb(pThis->RootHub.pIRhConn, pEd->hwinfo & ED_HWINFO_FUNCTION, Buf.cbTotal, 1);
if (!pUrb)
return false; /* retry later... */
#ifdef LOG_ENABLED
static unsigned s_iSerial = 0;
#endif
/* copy data if out bound transfer. */
&& enmDir != VUSBDIRECTION_IN)
{
}
/*
* Submit the URB.
*/
Log(("%s: ohciServiceTd: submitting TdAddr=%#010x EdAddr=%#010x cbData=%#x\n",
if (RT_SUCCESS(rc))
return true;
/* Failure cleanup. Can happen if we're still resetting the device or out of resources. */
Log(("ohciServiceTd: failed submitting TdAddr=%#010x EdAddr=%#010x pUrb=%p!!\n",
return false;
}
/**
* Service a the head TD of an endpoint.
*/
static bool ohciServiceHeadTd(POHCI pThis, VUSBXFERTYPE enmType, PCOHCIED pEd, uint32_t EdAddr, const char *pszListName)
{
/*
* Read the TD, after first checking if it's already in-flight.
*/
return false;
#if defined(VBOX_STRICT) || defined(LOG_ENABLED)
#endif
}
/**
* Service one or more general transport descriptors (bulk or interrupt).
*/
{
/*
* Read the TDs involved in this URB.
*/
struct OHCITDENTRY
{
/** The TD. */
/** The associated OHCI buffer tracker. */
/** The TD address. */
/** Pointer to the next element in the chain (stack). */
struct OHCITDENTRY *pNext;
} Head;
/* read the head */
/* combine with more TDs. */
unsigned cTds = 1;
&& cTds < 128)
{
/* don't combine if the direction doesn't match up. */
break;
cTds++;
}
/* calc next TD address */
/*
* Determine the direction.
*/
{
default:
Log(("ohciServiceTdMultiple: WARNING! Ed.hwdinfo=%#x bulk or interrupt EP shouldn't rely on the TD for direction...\n", pEd->hwinfo));
{
default:
Log(("ohciServiceTdMultiple: Invalid direction!!!! Head.Td.hwinfo=%#x Ed.hwdinfo=%#x\n", Head.Td.hwinfo, pEd->hwinfo));
/* TODO: Do the correct thing here */
return false;
}
break;
}
/*
* Allocate and initialize a new URB.
*/
PVUSBURB pUrb = VUSBIRhNewUrb(pThis->RootHub.pIRhConn, pEd->hwinfo & ED_HWINFO_FUNCTION, cbTotal, cTds);
if (!pUrb)
/* retry later... */
return false;
#ifdef LOG_ENABLED
static unsigned s_iSerial = 0;
#endif
/* Copy data and TD information. */
unsigned iTd = 0;
{
/* data */
if ( cbTotal
&& enmDir != VUSBDIRECTION_IN
{
}
/* TD info */
}
/*
* Submit the URB.
*/
Log(("%s: ohciServiceTdMultiple: submitting cbData=%#x EdAddr=%#010x cTds=%d TdAddr0=%#010x\n",
if (RT_SUCCESS(rc))
return true;
/* Failure cleanup. Can happen if we're still resetting the device or out of resources. */
Log(("ohciServiceTdMultiple: failed submitting pUrb=%p cbData=%#x EdAddr=%#010x cTds=%d TdAddr0=%#010x - rc=%Rrc\n",
return false;
}
/**
* Service the head TD of an endpoint.
*/
static bool ohciServiceHeadTdMultiple(POHCI pThis, VUSBXFERTYPE enmType, PCOHCIED pEd, uint32_t EdAddr, const char *pszListName)
{
/*
* First, check that it's not already in-flight.
*/
return false;
#if defined(VBOX_STRICT) || defined(LOG_ENABLED)
#endif
}
/**
* A worker for ohciServiceIsochronousEndpoint which unlinks a ITD
* that belongs to the past.
*/
static bool ohciServiceIsochronousTdUnlink(POHCI pThis, POHCIITD pITd, uint32_t ITdAddr, uint32_t ITdAddrPrev,
{
LogFlow(("%s%sohciServiceIsochronousTdUnlink: Unlinking ITD: ITdAddr=%#010x EdAddr=%#010x ITdAddrPrev=%#010x\n",
/*
* Do the unlinking.
*/
if (ITdAddrPrev)
{
/* Get validate the previous TD */
return false;
/* Update the copy and write it back. */
}
else
{
/* It's the head node. update the copy from the caller and write it back. */
}
/*
* If it's in flight, just mark the URB as unlinked (there is only one ITD per URB atm).
* Otherwise, retire it to the done queue with an error and cause a done line interrupt (?).
*/
if (pUrb)
{
return false;
}
else
{
}
return true;
}
/**
* A worker for ohciServiceIsochronousEndpoint which submits the specified TD.
*
* @returns true on success.
* @returns false on failure to submit.
* @param R The start packet (frame) relative to the start of frame in HwInfo.
*/
static bool ohciServiceIsochronousTd(POHCI pThis, POHCIITD pITd, uint32_t ITdAddr, const unsigned R, PCOHCIED pEd, uint32_t EdAddr)
{
/*
* Determine the endpoint direction.
*/
{
default:
/* Should probably raise an unrecoverable HC error here */
return false;
}
/*
* Extract the packet sizes and calc the total URB size.
*/
struct
{
} aPkts[ITD_NUM_PSW];
/* first entry (R) */
Log(("ITdAddr=%RX32 PSW%d.CC=%#x < 'Not Accessed'!\n", ITdAddr, R, pITd->aPSW[R] >> ITD_PSW_CC_SHIFT)); /* => Unrecoverable Error*/
/* R+1..cFrames */
{
Log(("ITdAddr=%RX32 PSW%d.offset=%#x < offPrev=%#x!\n", ITdAddr, iR, off, offPrev)); /* => Unrecoverable Error*/
Log(("ITdAddr=%RX32 PSW%d.CC=%#x < 'Not Accessed'!\n", ITdAddr, iR, PSW >> ITD_PSW_CC_SHIFT)); /* => Unrecoverable Error*/
}
/* calc offEnd and figure out the size of the last packet. */
+ 1 /* BE is inclusive */;
Log(("ITdAddr=%RX32 offEnd=%#x < offPrev=%#x!\n", ITdAddr, offEnd, offPrev)); /* => Unrecoverable Error*/
/*
* Allocate and initialize a new URB.
*/
PVUSBURB pUrb = VUSBIRhNewUrb(pThis->RootHub.pIRhConn, pEd->hwinfo & ED_HWINFO_FUNCTION, cbTotal, 1);
if (!pUrb)
/* retry later... */
return false;
pUrb->fShortNotOk = false;
#if 0 /* color the data */
#endif
#ifdef LOG_ENABLED
static unsigned s_iSerial = 0;
RTStrAPrintf(&pUrb->pszDesc, "URB %p isoc%c%04d", pUrb, enmDir == VUSBDIRECTION_IN ? '<' : '>', s_iSerial);
#endif
/* copy the data */
if ( cbTotal
&& enmDir != VUSBDIRECTION_IN)
{
if (off0 < 0x1000)
{
if (offEnd > 0x1000)
{
/* both pages. */
}
else /* a portion of the 1st page. */
}
else /* a portion of the 2nd page. */
}
/* setup the packets */
unsigned off = 0;
{
}
/*
* Submit the URB.
*/
Log(("%s: ohciServiceIsochronousTd: submitting cbData=%#x cIsocPkts=%d EdAddr=%#010x TdAddr=%#010x SF=%#x (%#x)\n",
pUrb->pszDesc, pUrb->cbData, pUrb->cIsocPkts, EdAddr, ITdAddr, pITd->HwInfo & ITD_HWINFO_SF, pThis->HcFmNumber));
if (RT_SUCCESS(rc))
return true;
/* Failure cleanup. Can happen if we're still resetting the device or out of resources. */
Log(("ohciServiceIsochronousTd: failed submitting pUrb=%p cbData=%#x EdAddr=%#010x cTds=%d ITdAddr0=%#010x - rc=%Rrc\n",
return false;
}
/**
* Service an isochronous endpoint.
*/
{
/*
* We currently process this as if the guest follows the interrupt end point chaining
* hierarchy described in the documenation. This means that for an isochronous endpoint
* with a 1 ms interval we expect to find in-flight TDs at the head of the list. We will
* skip over all in-flight TDs which timeframe has been exceed. Those which aren't in
* flight but which are too late will be retired (possibly out of order, but, we don't
* care right now).
*
* When we reach a TD which still has a buffer which is due for take off, we will
* stop iterating TDs. If it's in-flight, there isn't anything to be done. Otherwise
* we will push it onto the runway for immediate take off. In this process we
* might have to complete buffers which didn't make it on time, something which
* complicates the kind of status info we need to keep around for the TD.
*
* Note: We're currently not making any attempt at reassembling ITDs into URBs.
* However, this will become necessary because of EMT scheduling and guest
* like linux using one TD for each frame (simple but inefficient for us).
*/
uint32_t ITdAddrPrev = 0;
for (;;)
{
/* check for end-of-chain. */
|| !ITdAddr)
break;
/*
* If isochronous endpoints are around, don't slow down the timer. Getting the timing right
* is difficult enough as it is.
*/
/*
* Read the current ITD and check what we're supposed to do about it.
*/
if (R < cFrames)
{
/*
* It's inside the current or a future launch window.
*
* We will try maximize the TD in flight here to deal with EMT scheduling
* issues and similar stuff which will screw up the time. So, we will only
* stop submitting TD when we reach a gap (in time) or end of the list.
*/
if ( R < 0 /* (a future frame) */
break;
break;
}
else
{
#if 1
/*
* Ok, the launch window for this TD has passed.
* If it's not in flight it should be retired with a DataOverrun status (TD).
*
* Don't remove in-flight TDs before they complete.
* Windows will, upon the completion of another ITD it seems, check for if
* any other TDs has been unlinked. If we unlink them before they really
* complete all the packet status codes will be NotAccessed and Windows
* will fail the URB with status USBD_STATUS_ISOCH_REQUEST_FAILED.
*
* I don't know if unlinking TDs out of order could cause similar problems,
* time will show.
*/
if (iInFlight >= 0)
{
Log(("ohciServiceIsochronousEndpoint: Failed unlinking old ITD.\n"));
break;
}
#else /* BAD IDEA: */
/*
* Ok, the launch window for this TD has passed.
* If it's not in flight it should be retired with a DataOverrun status (TD).
*
* If it's in flight we will try unlink it from the list prematurely to
* help the guest to move on and shorten the list we have to walk. We currently
* are successful with the first URB but then it goes too slowly...
*/
{
Log(("ohciServiceIsochronousEndpoint: Failed unlinking old ITD.\n"));
break;
}
#endif
}
/* advance to the next ITD */
}
}
/**
* Checks if a endpoints has TDs queued and is ready to have them processed.
*
* @returns true if it's ok to process TDs.
* @param pEd The endpoint data.
*/
{
}
/**
* Checks if an endpoint has TDs queued (not necessarily ready to have them processed).
*
* @returns true if endpoint may have TDs queued.
* @param pEd The endpoint data.
*/
{
}
/**
* Services the bulk list.
*
* On the bulk list we must reassemble URBs from multiple TDs using heuristics
* derived from USB tracing done in the guests and guest source code (when available).
*/
{
#ifdef LOG_ENABLED
if (g_fLogBulkEPs)
Log(("ohciServiceBulkList: bulk_cur=%#010x before listprocessing!!! HCD have positioned us!!!\n", pThis->bulk_cur));
#endif
/*
* ", HC will start processing the Bulk list and will set BF [BulkListFilled] to 0"
* - We've simplified and are always starting at the head of the list and working
* our way thru to the end each time.
*/
pThis->fBulkNeedsCleaning = false;
while (EdAddr)
{
if (ohciIsEdReady(&Ed))
{
pThis->fBulkNeedsCleaning = true;
#if 1
/*
* After we figured out that all the TDs submitted for dealing with MSD
* reassemble these TDs into an URB before submitting it, there is no
* longer any need for servicing anything other than the head *URB*
* on a bulk endpoint.
*/
#else
/*
* This alternative code was used before we started reassembling URBs from
* multiple TDs. We keep it handy for debugging.
*/
{
do
{
{
LogFlow(("ohciServiceBulkList: ohciServiceTdMultiple -> false\n"));
break;
}
|| !TdAddr /* paranoia */)
{
break;
}
} while (ohciIsEdReady(&Ed));
}
#endif
}
else
{
{
/* If the ED is in 'skip' state, no transactions on it are allowed and we must
* cancel outstanding URBs, if any.
*/
if (pUrb)
}
}
/* next end point */
}
#ifdef LOG_ENABLED
if (g_fLogBulkEPs)
#endif
}
/**
* Abort outstanding transfers on the bulk list.
*
* If the guest disabled bulk list processing, we must abort any outstanding transfers
* (that is, cancel in-flight URBs associated with the list). This is required because
* there may be outstanding read URBs that will never get a response from the device
* and would block further communication.
*/
{
#ifdef LOG_ENABLED
if (g_fLogBulkEPs)
Log(("ohciUndoBulkList: bulk_cur=%#010x before list processing!!! HCD has positioned us!!!\n", pThis->bulk_cur));
#endif
/* This flag follows OHCI_STATUS_BLF, but BLF doesn't change when list processing is disabled. */
pThis->fBulkNeedsCleaning = false;
while (EdAddr)
{
if (ohciIsEdPresent(&Ed))
{
{
if (pUrb)
}
}
/* next endpoint */
}
}
/**
* Services the control list.
*
* The control list has complex URB assembling, but that's taken
* care of at VUSB level (unlike the other transfer types).
*/
{
#ifdef LOG_ENABLED
if (g_fLogControlEPs)
Log(("ohciServiceCtrlList: ctrl_cur=%010x before list processing!!! HCD have positioned us!!!\n", pThis->ctrl_cur));
#endif
/*
* ", HC will start processing the list and will set ControlListFilled to 0"
* - We've simplified and are always starting at the head of the list and working
* our way thru to the end each time.
*/
while (EdAddr)
{
if (ohciIsEdReady(&Ed))
{
#if 1
/*
* Control TDs depends on order and stage. Only one can be in-flight
* at any given time. OTOH, some stages are completed immediately,
* so we process the list until we've got a head which is in-flight
* or reach the end of the list.
*/
do
{
{
break;
}
} while (ohciIsEdReady(&Ed));
#else
/* Simplistic, for debugging. */
#endif
}
/* next end point */
}
#ifdef LOG_ENABLED
if (g_fLogControlEPs)
#endif
}
/**
* Services the periodic list.
*
* On the interrupt portion of the periodic list we must reassemble URBs from multiple
* TDs using heuristics derived from USB tracing done in the guests and guest source
* code (when available).
*/
{
/*
* Read the list head from the HCCA.
*/
#ifdef LOG_ENABLED
if (g_fLogInterruptEPs)
{
char sz[48];
}
#endif
/*
* Iterate the endpoint list.
*/
while (EdAddr)
{
if (ohciIsEdReady(&Ed))
{
/*
* "There is no separate head pointer of isochronous transfers. The first
* isochronous Endpoint Descriptor simply links to the last interrupt
* Endpoint Descriptor."
*/
{
/*
* Presently we will only process the head URB on an interrupt endpoint.
*/
}
{
/*
* Presently only the head ITD.
*/
}
else
break;
}
else
{
{
/* If the ED is in 'skip' state, no transactions on it are allowed and we must
* cancel outstanding URBs, if any.
*/
if (pUrb)
}
}
/* next end point */
}
#ifdef LOG_ENABLED
if (g_fLogInterruptEPs)
{
char sz[48];
}
#endif
}
/**
* Update the HCCA.
*
* @param pThis The OHCI instance data.
*/
{
bool fWriteDoneHeadInterrupt = false;
{
done |= 0x1;
#ifdef LOG_ENABLED
#endif
#if defined(VBOX_STRICT) || defined(LOG_ENABLED)
#endif
fWriteDoneHeadInterrupt = true;
}
}
/**
* Calculate frame timer variables given a frame rate (1,000 Hz is the full speed).
*/
{
if (!pThis->cTicksPerFrame)
}
/**
* Go over the in-flight URB list and cancel any URBs that are no longer in use.
* This occurs when the host removes EDs or TDs from the lists and we don't notice
* the sKip bit. Such URBs must be promptly canceled, otherwise there is a risk
* they might "steal" data destined for another URB.
*/
{
unsigned i, cLeft;
int j;
/* If the HCCA is not currently valid, or there are no in-flight URBs,
* there's nothing to do.
*/
return;
/* Initially mark all in-flight URBs as inactive. */
{
{
cLeft--;
}
}
* is marked as active again.
*/
for (i = 0; i < OHCI_HCCA_NUM_INTR + 2; i++)
{
switch (i)
{
case OHCI_HCCA_NUM_INTR:
break;
case OHCI_HCCA_NUM_INTR + 1:
break;
default:
break;
}
while (EdAddr)
{
unsigned k = 0;
{
do
{
if (j > -1)
/* Failsafe for temporarily looped lists. */
if (++k == 128)
break;
}
}
}
/* In-flight URBs still marked as inactive are not used anymore and need
* to be canceled.
*/
{
{
cLeft--;
}
}
}
/**
* Generate a Start-Of-Frame event, and set a timer for End-Of-Frame.
*/
{
#ifdef LOG_ENABLED
#endif
/*
* Update HcFmRemaining.FRT and update start of frame time.
*/
/*
* Check that the HCCA address isn't bogus. Linux 2.4.x is known to start
* the bus with a hcca of 0 to work around problem with a specific controller.
*/
#if 1
/*
* Update the HCCA.
* Should be done after SOF but before HC read first ED in this frame.
*/
if (fValidHCCA)
#endif
/* "After writing to HCCA, HC will set SF in HcInterruptStatus" - guest isn't executing, so ignore the order! */
{
}
/* If the HCCA address is invalid, we're quitting here to avoid doing something which cannot be reported to the HCD. */
if (!fValidHCCA)
{
Log(("ohciStartOfFrame: skipping hcca part because hcca=%RX32 (our 'valid' range: %RX32-%RX32)\n",
return;
}
/*
* Periodic EPs.
*/
/*
* Control EPs.
*/
/*
* Bulk EPs.
*/
&& pThis->fBulkNeedsCleaning)
#if 0
/*
* Update the HCCA after processing the lists and everything. A bit experimental.
*
* ASSUME the guest won't be very upset if a TD is completed, retired and handed
* of a control transfer together with the setup stage, thus saving a frame. This
* behaviour is should be perfectly ok, since the setup (and maybe data) stages
* have already taken at least one frame to complete.
*
* But, when implementing the first synchronous virtual USB devices, we'll have to
* verify that the guest doesn't choke when having a TD returned in the same frame
* as it was submitted.
*/
#endif
#ifdef LOG_ENABLED
{
Log2(("ohciStartOfFrame: HcCommandStatus=%#010x: %sHCR=%d %sCLF=%d %sBLF=%d %sOCR=%d %sSOC=%d\n",
val,
}
#endif
/*
* Adjust the frame timer interval based on idle detection.
*/
{
pThis->cIdleCycles++;
/* Set the new frame rate based on how long we've been idle. Tunable. */
switch (pThis->cIdleCycles)
{
default: break;
}
/* Avoid overflow. */
}
else
{
if (pThis->cIdleCycles)
{
pThis->cIdleCycles = 0;
}
}
}
/**
* Updates the HcFmNumber and FNO registers.
*/
{
}
{
int rc = VINF_SUCCESS;
uint64_t tsNanoStart = 0;
return VINF_SUCCESS;
{
&& RT_SUCCESS(rc))
break;
tsNanoStart = RTTimeNanoTS();
/* Reset idle detection flag */
/* Frame boundary, so do EOF stuff here. */
/* Clean up any URBs that have been removed. */
/* Start the next frame. */
/* Wait for the next round. */
}
return VINF_SUCCESS;
}
/**
* Unblock the framer thread so it can respond to a state change.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param pThread The send thread.
*/
{
}
/**
* Do frame processing on frame boundary
*/
{
}
/**
* Start sending SOF tokens across the USB bus, lists are processed in
* next frame
*/
{
}
/**
* Stop sending SOF tokens on the bus
*/
{
}
/**
* Move in to resume state
*/
{
Log(("pThis: ohciBusResume fHardware=%RTbool RWE=%s\n",
}
/* Power a port up or down */
{
if (fPowerUp)
{
/* power up */
}
else
{
/* power down */
}
}
#endif /* IN_RING3 */
/**
* Read the HcRevision register.
*/
{
Log2(("HcRevision_r() -> 0x10\n"));
return VINF_SUCCESS;
}
/**
* Write to the HcRevision register.
*/
{
return VINF_SUCCESS;
}
/**
* Read the HcControl register.
*/
{
Log2(("HcControl_r -> %#010x - CBSR=%d PLE=%d IE=%d CLE=%d BLE=%d HCFS=%#x IR=%d RWC=%d RWE=%d\n",
ctl, ctl & 3, (ctl >> 2) & 1, (ctl >> 3) & 1, (ctl >> 4) & 1, (ctl >> 5) & 1, (ctl >> 6) & 3, (ctl >> 8) & 1,
return VINF_SUCCESS;
}
/**
* Write the HcControl register.
*/
{
/* log it. */
Log2(("HcControl_w(%#010x) => %sCBSR=%d %sPLE=%d %sIE=%d %sCLE=%d %sBLE=%d %sHCFS=%#x %sIR=%d %sRWC=%d %sRWE=%d\n",
val,
if (val & ~0x07ff)
/* see what changed and take action on that. */
#ifdef IN_RING3
{
switch (new_state)
{
case OHCI_USB_OPERATIONAL:
LogRel(("OHCI: USB Operational\n"));
break;
case OHCI_USB_SUSPEND:
LogRel(("OHCI: USB Suspended\n"));
break;
case OHCI_USB_RESUME:
LogRel(("OHCI: USB Resume\n"));
break;
case OHCI_USB_RESET:
{
LogRel(("OHCI: USB Reset\n"));
/** @todo This should probably do a real reset, but we don't implement
* that correctly in the roothub reset callback yet. check it's
* comments and argument for more details. */
break;
}
}
}
#else /* !IN_RING3 */
{
Log2(("HcControl_w: state changed -> VINF_IOM_R3_MMIO_WRITE\n"));
return VINF_IOM_R3_MMIO_WRITE;
}
#endif /* !IN_RING3 */
return VINF_SUCCESS;
}
/**
* Read the HcCommandStatus register.
*/
{
Log2(("HcCommandStatus_r() -> %#010x - HCR=%d CLF=%d BLF=%d OCR=%d SOC=%d\n",
return VINF_SUCCESS;
}
/**
* Write to the HcCommandStatus register.
*/
{
/* log */
Log2(("HcCommandStatus_w(%#010x) => %sHCR=%d %sCLF=%d %sBLF=%d %sOCR=%d %sSOC=%d\n",
val,
if (val & ~0x0003000f)
/* SOC is read-only */
#ifdef IN_RING3
/* "bits written as '0' remain unchanged in the register" */
{
LogRel(("OHCI: Software reset\n"));
}
#else
{
LogFlow(("HcCommandStatus_w: reset -> VINF_IOM_R3_MMIO_WRITE\n"));
return VINF_IOM_R3_MMIO_WRITE;
}
#endif
return VINF_SUCCESS;
}
/**
* Read the HcInterruptStatus register.
*/
{
Log2(("HcInterruptStatus_r() -> %#010x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d\n",
return VINF_SUCCESS;
}
/**
* Write to the HcInterruptStatus register.
*/
{
Log2(("HcInterruptStatus_w(%#010x) => %sSO=%d %sWDH=%d %sSF=%d %sRD=%d %sUE=%d %sFNO=%d %sRHSC=%d %sOC=%d\n",
val,
if ( (val & ~0xc000007f)
/* "The Host Controller Driver may clear specific bits in this
* register by writing '1' to bit positions to be cleared"
*/
return VINF_SUCCESS;
}
/**
* Read the HcInterruptEnable register
*/
{
Log2(("HcInterruptEnable_r() -> %#010x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d MIE=%d\n",
return VINF_SUCCESS;
}
/**
* Writes to the HcInterruptEnable register.
*/
{
Log2(("HcInterruptEnable_w(%#010x) => %sSO=%d %sWDH=%d %sSF=%d %sRD=%d %sUE=%d %sFNO=%d %sRHSC=%d %sOC=%d %sMIE=%d\n",
val,
if (val & ~0xc000007f)
return VINF_SUCCESS;
}
/**
* Reads the HcInterruptDisable register.
*/
{
#if 1 /** @todo r=bird: "On read, the current value of the HcInterruptEnable register is returned." */
#else /* old code. */
#endif
Log2(("HcInterruptDisable_r() -> %#010x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d MIE=%d\n",
return VINF_SUCCESS;
}
/**
* Writes to the HcInterruptDisable register.
*/
{
Log2(("HcInterruptDisable_w(%#010x) => %sSO=%d %sWDH=%d %sSF=%d %sRD=%d %sUE=%d %sFNO=%d %sRHSC=%d %sOC=%d %sMIE=%d\n",
val,
/* Don't bitch about invalid bits here since it makes sense to disable
* interrupts you don't know about. */
return VINF_SUCCESS;
}
/**
* Read the HcHCCA register (Host Controller Communications Area physical address).
*/
{
return VINF_SUCCESS;
}
/**
* Write to the HcHCCA register (Host Controller Communications Area physical address).
*/
{
return VINF_SUCCESS;
}
/**
* Read the HcPeriodCurrentED register.
*/
{
return VINF_SUCCESS;
}
/**
* Write to the HcPeriodCurrentED register.
*/
{
Log(("HcPeriodCurrentED_w(%#010x) - old=%#010x new=%#010x (This is a read only register, only the linux guys don't respect that!)\n",
//AssertMsgFailed(("HCD (Host Controller Driver) should not write to HcPeriodCurrentED! val=%#010x (old=%#010x)\n", val, pThis->per_cur));
return VINF_SUCCESS;
}
/**
* Read the HcControlHeadED register.
*/
{
return VINF_SUCCESS;
}
/**
* Write to the HcControlHeadED register.
*/
{
return VINF_SUCCESS;
}
/**
* Read the HcControlCurrentED register.
*/
{
return VINF_SUCCESS;
}
/**
* Write to the HcControlCurrentED register.
*/
{
AssertMsg(!(pThis->ctl & OHCI_CTL_CLE), ("Illegal write! HcControl.ControlListEnabled is set! val=%#010x\n", val));
return VINF_SUCCESS;
}
/**
* Read the HcBulkHeadED register.
*/
{
return VINF_SUCCESS;
}
/**
* Write to the HcBulkHeadED register.
*/
{
pThis->bulk_head = val & ~7; /** @todo The ATI OHCI controller on my machine enforces 16-byte address alignment. */
return VINF_SUCCESS;
}
/**
* Read the HcBulkCurrentED register.
*/
{
return VINF_SUCCESS;
}
/**
* Write to the HcBulkCurrentED register.
*/
{
AssertMsg(!(pThis->ctl & OHCI_CTL_BLE), ("Illegal write! HcControl.BulkListEnabled is set! val=%#010x\n", val));
return VINF_SUCCESS;
}
/**
* Read the HcDoneHead register.
*/
{
return VINF_SUCCESS;
}
/**
* Write to the HcDoneHead register.
*/
{
/*AssertMsgFailed(("Illegal operation!!! val=%#010x\n", val)); - OS/2 does this */
return VINF_SUCCESS;
}
/**
* Read the HcFmInterval (Fm=Frame) register.
*/
{
Log2(("HcFmInterval_r() -> 0x%#08x - FI=%d FSMPS=%d FIT=%d\n",
return VINF_SUCCESS;
}
/**
* Write to the HcFmInterval (Fm = Frame) register.
*/
{
/* log */
Log2(("HcFmInterval_w(%#010x) => %sFI=%d %sFSMPS=%d %sFIT=%d\n",
val,
{
}
/* update */
return VINF_SUCCESS;
}
/**
* Read the HcFmRemaining (Fm = Frame) register.
*/
{
{
/*
* Being in USB operational state guarantees SofTime was set already.
*/
{
}
}
return VINF_SUCCESS;
}
/**
* Write to the HcFmRemaining (Fm = Frame) register.
*/
{
return VINF_SUCCESS;
}
/**
* Read the HcFmNumber (Fm = Frame) register.
*/
{
Log2(("HcFmNumber_r() -> %#010x - FN=%#x(%d) (32-bit=%#x(%d))\n", val, val, val, pThis->HcFmNumber, pThis->HcFmNumber));
return VINF_SUCCESS;
}
/**
* Write to the HcFmNumber (Fm = Frame) register.
*/
{
return VINF_SUCCESS;
}
/**
* Read the HcPeriodicStart register.
* The register determines when in a frame to switch from control&bulk to periodic lists.
*/
{
return VINF_SUCCESS;
}
/**
* Write to the HcPeriodicStart register.
* The register determines when in a frame to switch from control&bulk to periodic lists.
*/
{
if (val & ~0x3fff)
return VINF_SUCCESS;
}
/**
* Read the HcLSThreshold register.
*/
{
return VINF_SUCCESS;
}
/**
* Write to the HcLSThreshold register.
*
* Docs are inconsistent here:
*
* "Neither the Host Controller nor the Host Controller Driver are allowed to change this value."
*
* "This value is calculated by HCD with the consideration of transmission and setup overhead."
*
* The register is marked "R/W" the HCD column.
*
*/
{
("HCD tried to write bad LS threshold: 0x%x (see function header)\n", val));
/** @todo the HCD can change this. */
return VINF_SUCCESS;
}
/**
* Read the HcRhDescriptorA register.
*/
{
#if 0 /* annoying */
Log2(("HcRhDescriptorA_r() -> %#010x - NDP=%d PSM=%d NPS=%d DT=%d OCPM=%d NOCP=%d POTGT=%#x\n",
#endif
return VINF_SUCCESS;
}
/**
* Write to the HcRhDescriptorA register.
*/
{
Log2(("HcRhDescriptorA_w(%#010x) => %sNDP=%d %sPSM=%d %sNPS=%d %sDT=%d %sOCPM=%d %sNOCP=%d %sPOTGT=%#x - %sPowerSwitching Set%sPower\n",
val,
if (val & ~0xff001fff)
{
Log(("ohci: %s: invalid write to NDP or DT in roothub descriptor A!!! val=0x%.8x\n",
}
return VINF_SUCCESS;
}
/**
* Read the HcRhDescriptorB register.
*/
{
Log2(("HcRhDescriptorB_r() -> %#010x - DR=0x%04x PPCM=0x%04x\n",
return VINF_SUCCESS;
}
/**
* Write to the HcRhDescriptorB register.
*/
{
Log2(("HcRhDescriptorB_w(%#010x) => %sDR=0x%04x %sPPCM=0x%04x\n",
val,
Log(("ohci: %s: unsupported write to root descriptor B!!! 0x%.8x -> 0x%.8x\n",
return VINF_SUCCESS;
}
/**
* Read the HcRhStatus (Rh = Root Hub) register.
*/
{
Log2(("HcRhStatus_r() -> %#010x - LPS=%d OCI=%d DRWE=%d LPSC=%d OCIC=%d CRWE=%d\n",
return VINF_SUCCESS;
}
/**
* Write to the HcRhStatus (Rh = Root Hub) register.
*/
{
#ifdef IN_RING3
/* log */
if (val & ~0x80038003)
/* write 1 to clear OCIC */
if ( val & OHCI_RHS_OCIC )
/* SetGlobalPower */
if ( val & OHCI_RHS_LPSC )
{
int i;
for (i = 0; i < OHCI_NDP; i++)
}
/* ClearGlobalPower */
if ( val & OHCI_RHS_LPS )
{
int i;
for (i = 0; i < OHCI_NDP; i++)
}
if ( val & OHCI_RHS_DRWE )
if ( val & OHCI_RHS_CRWE )
Log2(("HcRhStatus_w(%#010x) => %sCGP=%d %sOCI=%d %sSRWE=%d %sSGP=%d %sOCIC=%d %sCRWE=%d\n",
val,
return VINF_SUCCESS;
#else /* !IN_RING3 */
return VINF_IOM_R3_MMIO_WRITE;
#endif /* !IN_RING3 */
}
/**
* Read the HcRhPortStatus register of a port.
*/
{
const unsigned i = iReg - 21;
uint32_t val = pThis->RootHub.aPorts[i].fReg | OHCI_PORT_R_POWER_STATUS; /* PortPowerStatus: see todo on power in _w function. */
if (val & OHCI_PORT_R_RESET_STATUS)
{
#ifdef IN_RING3
#else
Log2(("HcRhPortStatus_r: yield -> VINF_IOM_R3_MMIO_READ\n"));
return VINF_IOM_R3_MMIO_READ;
#endif
}
if (val & (OHCI_PORT_R_RESET_STATUS | OHCI_PORT_CSC | OHCI_PORT_PESC | OHCI_PORT_PSSC | OHCI_PORT_OCIC | OHCI_PORT_PRSC))
Log2(("HcRhPortStatus_r(): port %u: -> %#010x - CCS=%d PES=%d PSS=%d POCI=%d RRS=%d PPS=%d LSDA=%d CSC=%d PESC=%d PSSC=%d OCIC=%d PRSC=%d\n",
i, val, val & 1, (val >> 1) & 1, (val >> 2) & 1, (val >> 3) & 1, (val >> 4) & 1, (val >> 8) & 1, (val >> 9) & 1,
return VINF_SUCCESS;
}
#ifdef IN_RING3
/**
* Completion callback for the vusb_dev_reset() operation.
* @thread EMT.
*/
{
/*
* Find the port in question
*/
unsigned iPort;
{
break;
}
if (!pPort)
{
return;
}
if (RT_SUCCESS(rc))
{
/*
* Successful reset.
*/
Log2(("uchi_port_reset_done: Reset completed.\n"));
pPort->fReg &= ~(OHCI_PORT_R_RESET_STATUS | OHCI_PORT_R_SUSPEND_STATUS | OHCI_PORT_R_SUSPEND_STATUS_CHANGE);
}
else
{
/* desperate measures. */
{
/*
* Damn, something weird happened during reset. We'll pretend the user did an
* incredible fast reconnect or something. (probably not gonna work)
*/
Log2(("uchi_port_reset_done: The reset failed (rc=%Rrc)!!! Pretending reconnect at the speed of light.\n", rc));
}
else
{
/*
* The device have / will be disconnected.
*/
pPort->fReg &= ~(OHCI_PORT_R_RESET_STATUS | OHCI_PORT_R_SUSPEND_STATUS | OHCI_PORT_R_SUSPEND_STATUS_CHANGE | OHCI_PORT_R_RESET_STATUS_CHANGE);
}
}
/* Raise roothub status change interrupt. */
}
/**
* Sets a flag in a port status register but only set it if a device is
* connected, if not set ConnectStatusChange flag to force HCD to reevaluate
* connect status.
*
* @returns true if device was connected and the flag was cleared.
*/
{
/*
* Writing a 0 has no effect
*/
if (fValue == 0)
return false;
/*
* If CurrentConnectStatus is cleared we set ConnectStatusChange.
*/
{
return false;
}
/* set the bit */
return fRc;
}
#endif /* IN_RING3 */
/**
* Write to the HcRhPortStatus register of a port.
*/
{
#ifdef IN_RING3
const unsigned i = iReg - 21;
#ifdef LOG_ENABLED
/*
* Log it.
*/
static const char *apszCmdNames[32] =
{
"ClearPortEnable", "SetPortEnable", "SetPortSuspend", "!!!ClearSuspendStatus",
"SetPortReset", "!!!5", "!!!6", "!!!7",
"SetPortPower", "ClearPortPower", "!!!10", "!!!11",
"!!!12", "!!!13", "!!!14", "!!!15",
"ClearCSC", "ClearPESC", "ClearPSSC", "ClearOCIC",
"ClearPRSC", "!!!21", "!!!22", "!!!23",
"!!!24", "!!!25", "!!!26", "!!!27",
"!!!28", "!!!29", "!!!30", "!!!31"
};
for (unsigned j = 0; j < RT_ELEMENTS(apszCmdNames); j++)
if (val & (1 << j))
Log2(("\n"));
#endif
/* Write to clear any of the change bits: CSC, PESC, PSSC, OCIC and PRSC */
if (val & OHCI_PORT_W_CLEAR_CHANGE_MASK)
if (val & OHCI_PORT_W_CLEAR_ENABLE)
{
p->fReg &= ~OHCI_PORT_R_ENABLE_STATUS;
Log2(("HcRhPortStatus_w(): port %u: DISABLE\n", i));
}
Log2(("HcRhPortStatus_w(): port %u: ENABLE\n", i));
Log2(("HcRhPortStatus_w(): port %u: SUSPEND - not implemented correctly!!!\n", i));
if (val & OHCI_PORT_W_SET_RESET)
{
{
p->fReg &= ~OHCI_PORT_R_RESET_STATUS_CHANGE;
}
else if (p->fReg & OHCI_PORT_R_RESET_STATUS)
{
/* the guest is getting impatient. */
Log2(("HcRhPortStatus_w(): port %u: Impatient guest!\n"));
}
}
{
/** @todo To implement per-device power-switching
* we need to check PortPowerControlMask to make
* sure it isn't gang powered
*/
if (val & OHCI_PORT_W_CLEAR_POWER)
if (val & OHCI_PORT_W_SET_POWER)
}
/** @todo r=frank: ClearSuspendStatus. Timing? */
{
}
{
Log2(("HcRhPortStatus_w(%#010x): port %u: => %sCCS=%d %sPES=%d %sPSS=%d %sPOCI=%d %sRRS=%d %sPPS=%d %sLSDA=%d %sCSC=%d %sPESC=%d %sPSSC=%d %sOCIC=%d %sPRSC=%d\n",
val, i,
}
return VINF_SUCCESS;
#else /* !IN_RING3 */
return VINF_IOM_R3_MMIO_WRITE;
#endif /* !IN_RING3 */
}
/**
* Register descriptor table
*/
{
/* The number of port status register depends on the definition
* of OHCI_NDP macro
*/
};
/**
* @callback_method_impl{FNIOMMMIOREAD}
*/
PDMBOTHCBDECL(int) ohciMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
{
/* Paranoia: Assert that IOMMMIO_FLAGS_READ_DWORD works. */
/*
* Validate the register and call the read operator.
*/
int rc;
{
}
else
{
}
return rc;
}
/**
* @callback_method_impl{FNIOMMMIOWRITE}
*/
PDMBOTHCBDECL(int) ohciMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
{
/* Paranoia: Assert that IOMMMIO_FLAGS_WRITE_DWORD_ZEROED works. */
/*
* Validate the register and call the read operator.
*/
int rc;
{
}
else
{
rc = VINF_SUCCESS;
}
return rc;
}
#ifdef IN_RING3
/**
* @callback_method_impl{FNPCIIOREGIONMAP}
*/
static DECLCALLBACK(int) ohciR3Map(PPCIDEVICE pPciDev, int iRegion, RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
{
if (RT_FAILURE(rc))
return rc;
if (pThis->fRZEnabled)
{
if (RT_FAILURE(rc))
return rc;
if (RT_FAILURE(rc))
return rc;
}
return VINF_SUCCESS;
}
/**
* Prepares for state saving.
* All URBs needs to be canceled.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param pSSM The handle to save the state to.
*/
{
LogFlow(("ohciR3SavePrep: \n"));
/*
* Detach all proxied devices.
*/
/** @todo this won't work well when continuing after saving! */
{
if (pDev)
{
if (!VUSBIDevIsEmulated(pDev))
{
/*
* Save the device pointers here so we can reattach them afterwards.
* This will work fine even if the save fails since the Done handler is
* called unconditionally if the Prep handler was called.
*/
}
}
}
/*
* Kill old load data which might be hanging around.
*/
{
}
return VINF_SUCCESS;
}
/**
* Saves the state of the OHCI device.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param pSSM The handle to save the state to.
*/
{
LogFlow(("ohciR3SaveExec: \n"));
if (RT_SUCCESS(rc))
return rc;
}
/**
* Done state save operation.
*
* @returns VBox load code.
* @param pDevIns Device instance of the device which registered the data unit.
* @param pSSM SSM operation handle.
*/
{
unsigned i;
LogFlow(("ohciR3SaveDone: \n"));
/*
* NULL the dev pointers.
*/
/*
* Attach the devices.
*/
{
if ( pDev
&& !VUSBIDevIsEmulated(pDev))
}
return VINF_SUCCESS;
}
/**
* Prepare loading the state of the OHCI device.
* This must detach the devices currently attached and save
* the up for reconnect after the state load have been completed
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param pSSM The handle to the saved state.
* @param u32Version The data unit version number.
*/
{
int rc = VINF_SUCCESS;
LogFlow(("ohciR3LoadPrep:\n"));
{
unsigned i;
/*
* Detach all devices which are present in this session. Save them in the load
* structure so we can reattach them after restoring the guest.
*/
{
if ( pDev
&& !VUSBIDevIsEmulated(pDev))
{
}
}
/*
* Any devices to reattach, if so duplicate the Load struct.
*/
{
return VERR_NO_MEMORY;
}
}
/* else: we ASSUME no device can be attached or detach in the period
* between a state load and the pLoad stuff is processed. */
return rc;
}
/**
* Loads the state of the OHCI device.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param pSSM The handle to the saved state.
* @param uVersion The data unit version number.
* @param uPass The data pass.
*/
static DECLCALLBACK(int) ohciR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
{
int rc;
LogFlow(("ohciR3LoadExec:\n"));
if (uVersion == OHCI_SAVED_STATE_VERSION)
{
if (RT_FAILURE(rc))
return rc;
}
else if (uVersion == OHCI_SAVED_STATE_VERSION_MEM_HELL)
{
static SSMFIELD const s_aOhciFields22[] =
{
};
/* deserialize the struct */
rc = SSMR3GetStructEx(pSSM, pThis, sizeof(*pThis), SSMSTRUCT_FLAGS_NO_MARKERS /*fFlags*/, &s_aOhciFields22[0], NULL);
if (RT_FAILURE(rc))
return rc;
/* check delimiter */
if (RT_FAILURE(rc))
return rc;
}
else
/*
* Finally restore the timer.
*/
}
/**
* Done state load operation.
*
* @returns VBox load code.
* @param pDevIns Device instance of the device which registered the data unit.
* @param pSSM SSM operation handle.
*/
{
LogFlow(("ohciR3LoadDone:\n"));
/*
* Start a timer if we've got devices to reattach
*/
{
TMTIMER_FLAGS_NO_CRIT_SECT, "OHCI reattach devices on load",
if (RT_SUCCESS(rc))
return rc;
}
return VINF_SUCCESS;
}
/**
* Reattaches devices after a saved state load.
*/
static DECLCALLBACK(void) ohciR3LoadReattachDevices(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
{
LogFlow(("ohciR3LoadReattachDevices:\n"));
/*
* Reattach devices.
*/
/*
* Cleanup.
*/
}
/**
* Reset notification.
*
* @returns VBox status.
* @param pDevIns The device instance data.
*/
{
LogFlow(("ohciR3Reset:\n"));
/*
* There is no distinction between cold boot, warm reboot and software reboots,
* all of these are treated as cold boots. We are also doing the initialization
* job of a BIOS or SMM driver.
*
* Important: Don't confuse UsbReset with hardware reset. Hardware reset is
* just one way of getting into the UsbReset state.
*/
}
/**
* Info handler, device version. Dumps OHCI control registers.
*
* @param pDevIns Device instance which registered the info.
* @param pHlp Callback functions for doing output.
* @param pszArgs Argument string. Optional and specific to the handler.
*/
static DECLCALLBACK(void) ohciR3InfoRegs(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
{
/* Control register */
pHlp->pfnPrintf(pHlp, "HcControl: %08x - CBSR=%d PLE=%d IE=%d CLE=%d BLE=%d HCFS=%#x IR=%d RWC=%d RWE=%d\n",
ctl, ctl & 3, (ctl >> 2) & 1, (ctl >> 3) & 1, (ctl >> 4) & 1, (ctl >> 5) & 1, (ctl >> 6) & 3, (ctl >> 8) & 1,
/* Command status register */
/* Interrupt status register */
pHlp->pfnPrintf(pHlp, "HcInterruptStatus: %08x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d\n",
/* Interrupt enable register */
pHlp->pfnPrintf(pHlp, "HcInterruptEnable: %08x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d MIE=%d\n",
/* HCCA address register */
/* Current periodic ED register */
/* Control ED registers */
/* Bulk ED registers */
/* Done head register */
}
/**
* Relocate device instance data.
*
* @returns VBox status.
* @param pDevIns The device instance data.
* @param offDelta The relocation delta.
*/
{
}
/**
* Destruct a device instance.
*
* Most VM resources are freed by the VM. This callback is provided so that any non-VM
* resources can be freed correctly.
*
* @returns VBox status.
* @param pDevIns The device instance data.
*/
{
/*
* Destroy event sempahores.
*/
/*
* Tear down the per endpoint in-flight tracking...
*/
return VINF_SUCCESS;
}
/**
* @interface_method_impl{PDMDEVREG,pfnConstruct,OHCI constructor}
*/
{
/*
* Init instance data.
*/
#ifdef VBOX_WITH_MSI_DEVICES
#endif
/* USB LED */
/*
* Read configuration. No configuration keys are currently supported.
*/
/*
* Register PCI device and I/O region.
*/
if (RT_FAILURE(rc))
return rc;
#ifdef VBOX_WITH_MSI_DEVICES
if (RT_FAILURE(rc))
{
/* That's OK, we can work without MSI */
}
#endif
if (RT_FAILURE(rc))
return rc;
/*
* Create the end-of-frame timer.
*/
TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "USB Frame Timer",
if (RT_FAILURE(rc))
return rc;
/*
* Register the saved state data unit.
*/
if (RT_FAILURE(rc))
return rc;
/*
* Attach to the VBox USB RootHub Driver on LUN #0.
*/
if (RT_FAILURE(rc))
{
AssertMsgFailed(("Configuration error: No roothub driver attached to LUN #0!\n"));
return rc;
}
("Configuration error: The driver doesn't provide the VUSBIROOTHUBCONNECTOR interface!\n"),
("Configuration error: The driver doesn't provide the VUSBIDEVICE interface!\n"),
/*
* Attach status driver (optional).
*/
if (RT_SUCCESS(rc))
else if (rc != VERR_PDM_NO_ATTACHED_DRIVER)
{
return rc;
}
/*
* Calculate the timer intervals.
* This assumes that the VM timer doesn't change frequency during the run.
*/
Log(("ohci: cTicksPerFrame=%RU64 cTicksPerUsbTick=%RU64\n",
pThis->fBusStarted = false;
/*
* Initialize the critical section without the lock validator.
* This is necessary because USB devices attached to this controller
* will be detached in the save state callback with the
* per device PDM critical section held. If there are still URBs pending
* for this device they will get reaped and cause a lock validator error
* because they will take this critical section.
*
* The framer thread on the other hand will first take this critical section
* during a run and might take the PDM critical section when issuing an interrupt.
* Normally this is a real deadlock issue but we make sure
* that the framer thread is not running when the save state handler is called.
*/
if (RT_FAILURE(rc))
N_("OHCI: Failed to create critical section"));
if (RT_FAILURE(rc))
N_("OHCI: Failed to create worker thread"));
/*
* Do a hardware reset.
*/
#ifdef VBOX_WITH_STATISTICS
/*
* Register statistics.
*/
PDMDevHlpSTAMRegister(pDevIns, &pThis->StatCanceledIsocUrbs, STAMTYPE_COUNTER, "/Devices/OHCI/CanceledIsocUrbs", STAMUNIT_OCCURENCES, "Detected canceled isochronous URBs.");
PDMDevHlpSTAMRegister(pDevIns, &pThis->StatCanceledGenUrbs, STAMTYPE_COUNTER, "/Devices/OHCI/CanceledGenUrbs", STAMUNIT_OCCURENCES, "Detected canceled general URBs.");
PDMDevHlpSTAMRegister(pDevIns, &pThis->StatDroppedUrbs, STAMTYPE_COUNTER, "/Devices/OHCI/DroppedUrbs", STAMUNIT_OCCURENCES, "Dropped URBs (endpoint halted, or URB canceled).");
PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTimer, STAMTYPE_PROFILE, "/Devices/OHCI/Timer", STAMUNIT_TICKS_PER_CALL, "Profiling ohciFrameBoundaryTimer.");
#endif
/*
* Register debugger info callbacks.
*/
#if 0/*def DEBUG_bird*/
// g_fLogInterruptEPs = true;
g_fLogControlEPs = true;
g_fLogBulkEPs = true;
#endif
return VINF_SUCCESS;
}
const PDMDEVREG g_DeviceOHCI =
{
/* u32version */
/* szName */
"usb-ohci",
/* szRCMod */
"VBoxDDGC.gc",
/* szR0Mod */
"VBoxDDR0.r0",
/* pszDescription */
"OHCI USB controller.\n",
/* fFlags */
/* fClass */
/* cMaxInstances */
~0U,
/* cbInstance */
sizeof(OHCI),
/* pfnConstruct */
/* pfnDestruct */
/* pfnRelocate */
/* pfnMemSetup */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
/* pfnSuspend */
NULL,
/* pfnResume */
NULL,
/* pfnAttach */
NULL,
/* pfnDetach */
NULL,
/* pfnQueryInterface */
NULL,
/* pfnInitComplete */
NULL,
/* pfnPowerOff */
NULL,
/* pfnSoftReset */
NULL,
/* u32VersionEnd */
};
#endif /* IN_RING3 */
#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */