DevOHCI.cpp revision 82bcaaf8077ba892f39afb721dca149353c63d2c
* 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 * to control/bulk service ratios or bandwidth requirements since our USB * 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 * relevent 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 * 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. * 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 * bird: we do work synchronously now, anything causes guest crashes. /******************************************************************************* *******************************************************************************/ /******************************************************************************* * Structures and Typedefs * *******************************************************************************/ /** The saved state version. */ /** 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. */ /* Number of Downstream Ports on the root hub, if you change this * you need to add more status register words to the 'opreg' array /** Pointer to OHCI device data. */ /** The port register. */ /** The device attached to the port. */ /** Pointer to an OHCI hub port. */ * @implements VUSBIROOTHUBPORT * @implements PDMILEDPORTS /** 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. */ /** Pointer to the OHCI root hub. */ * Data used for reattaching devices on a state 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 /** Number of detached devices. */ /** Array of devices which were detached. */ /** Pointer to an OHCILOAD structure. */ /** 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. */ /** @name Control partition /** HcInterruptStatus. */ /** HcInterruptEnabled. */ /** @name Memory pointer partition /** HcPeriodCurrentEd. */ /** HcControlCurrentED. */ /** @name Frame counter partition /** HcFmInterval.FSMPS - FSLargestDataPacket */ /** HcFmInterval.FIT - FrameItervalToggle */ /** HcFmInterval.FI - FrameInterval */ /** HcFmRemaining.FRT - toggle bit. */ * @remark The register size is 16-bit, but for debugging and performance * reasons we maintain a 32-bit counter. */ /** The number of virtual time ticks per frame. */ /** The number of virtual time ticks per USB bus tick. */ /** Number of in-flight TDs. */ unsigned Alignment1;
/**< Align aInFlight on a 8 byte boundary. */ /** Array of in-flight TDs. */ /** Address of the transport descriptor. */ /** Pointer to the URB. */ /** Number of in-done-queue TDs. */ /** Array of in-done-queue TDs. */ /** Address of the transport descriptor. */ /** When the tail of the done queue was added. * Used to calculate the age of the done queue. */ /** Align pLoad, the stats and the struct size correctly. */ /** 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 */ /* Standard OHCI bus speed */ /* Host Controller Communications Area */ /** @name OHCI Endpoint Descriptor * OHCI Endpoint Descriptor. /** 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 Desciptor. Bits 0-3 ignored / preserved. */ /** @name Completion Codes /* 0x0a..0x0b - reserved */ /** @name OHCI General transfer descriptor /** Error count (EC) shift. */ /** Error count max. (One greater than what the EC field can hold.) */ /** CC - Condition code mask. */ /** DI - Delay interrupt. */ /** DP - Direction / PID. */ /** R - Buffer rounding. */ /** Bits that are reserved / unknown. */ /** SETUP - to endpoint. */ /** OUT - to endpoint. */ /** IN - from endpoint. */ * OHCI general transfer descriptor /** 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. */ /** DI - Delay interrupt. (TD_HWINFO_DI) */ /** CC - Condition code mask. (=TD_HWINFO_CC) */ /** The buffer page 0 mask (lower 12 bits are ignored). */ /** 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". /** SIZE field mask for IN bound transfers. * (Only valid when CC isn't Not Accessed.)*/ * USed to indicate the format of SIZE (Not Accessed -> OFFSET). */ * OHCI isochronous transfer descriptor. /** 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. /** SO - Scheduling overrun. */ /** 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. */ /** @name HcRhPortStatus[n] - RH Port Status register (read). /** CCS - CurrentConnectionStatus - 0 = no device, 1 = device. */ /** 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. */ /** 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. */ /** 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. */ /******************************************************************************* *******************************************************************************/ * SSM descriptor table for the OHCI structure. /******************************************************************************* *******************************************************************************/ /* Update host controller state to reflect a device attach */ Log2((
"ohci: Fired off interrupt %#010x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d - %s\n",
* Set an interrupt, use the wrapper ohciSetInterrupt. * Set an interrupt wrapper macro for logging purposes. /* Carry out a hardware remote wakeup */ * Query interface method for the roothub LUN. * 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. /** Converts a OHCI.roothub.IRhPort pointer to a POHCI. */ * Get the number of avilable 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. * Gets the supported USB versions. * @returns The mask of supported USB versions. * @param pInterface Pointer to this structure. * 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. * Validate and adjust input. * 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. * Validate and adjust input. * 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. LogRel((
"OHCI: root hub reset completed with %Rrc\n",
rc));
* @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! * We're prending 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 comming 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. * Does a software or hardware reset of the controller. * This is called in response to setting HcCommandStatus.HCR, hardware reset, * and device construction. * @param pOhci 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 * suspend/reset state. Also, a real HC isn't going to send anything * any more when a reset has been signaled. * Reset the hardware registers. pOhci->
fsmps =
0x2778;
/* To-Be-Defined, use the value linux sets...*/ pOhci->
fi =
11999;
/* (12MHz ticks, one frame is 1ms) */ * 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.) * Writes physical memory. * Read an array of dwords from physical memory and correct endianness. for(
int i = 0; i <
c32s; i++)
* Write an array of dwords from physical memory and correct endianness. Log3((
"ohciReadTd(,%#010x,): R=%d DP=%d DI=%d T=%d EC=%d CC=%#x CBP=%#010x NextTD=%#010x BE=%#010x UNK=%#x\n",
* 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" 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",
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",
Log3((
"ohciWriteTd(,%#010x,): %sR=%d %sDP=%d %sDI=%#x %sT=%d %sEC=%d %sCC=%#x %sCBP=%#010x %sNextTD=%#010x %sBE=%#010x (%s)\n",
Log3((
"ohciWriteITd(,%#010x,): %sSF=%#x (now=%#RX32) %sDI=%#x %sFC=%d %sCC=%#x %sBP0=%#010x %sNextTD=%#010x %sBE=%#010x (%s)\n",
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",
* Core TD queue dumper. LOG_ENABLED builds only. /* can't use ohciReadTd() because of Log4. */ Log4((
" [R=%d DP=%d DI=%d T=%d EC=%d CC=%#x CBP=%#010x NextTD=%#010x BE=%#010x] -> ",
* Dumps a TD queue. LOG_ENABLED builds only. * Core ITD queue dumper. LOG_ENABLED builds only. /* can't use ohciReadTd() because of Log4. */ Log4((" [R=%d DP=%d DI=%d T=%d EC=%d CC=%#x CBP=%#010x NextTD=%#010x BE=%#010x] -> ", * Dumps a ED list. LOG_ENABLED builds only. * Record an in-flight TD. * @param pOhci OHCI instance data. * @param GCPhysTD Physical address of the TD. * Record in-flight TDs for an URB. * @param pOhci OHCI instance data. * @returns Index of the record. * @returns -1 if not found. * @param pOhci OHCI instance data. * @param GCPhysTD Physical address of the TD. * @remark This has to be fast. * Checks if a TD is in-flight. * @returns true if in flight, false if not. * @param pOhci OHCI instance data. * @param GCPhysTD Physical address of the TD. * 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 pOhci OHCI instance data. * @param GCPhysTD Physical address of the TD. Log2((
"ohci_in_flight_remove: reaping TD=%#010x %d frames (%#010x-%#010x)\n",
* 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 pOhci OHCI instance data. * Empties the in-done-queue. * @param pOhci OHCI instance data. * Finds a TD in the in-done-queue. * @returns >= 0 on success. * @returns -1 if not found. * @param pOhci OHCI instance data. * @param GCPhysTD Physical address of the TD. * Checks that the specified TD is not in the done queue. * @param pOhci OHCI instance data. * @param GCPhysTD Physical address of the TD. * Adds a TD to the in-done-queue tracking, checking that it's not there already. * @param pOhci 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. /** The 32-bit physical address of this part. */ /** Number of valid entries in aVecs. */ * 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. Log2((
"ohci: cbp=%#010x be=%#010x cbTotal=0 EMPTY\n",
cbp,
be));
else if ((
cbp & ~
0xfff) == (
be & ~
0xfff))
* 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 parital transfer.) * @param pBuf The buffer to update. cbTotal contains the new total on input. * While the aVecs[*].cb members is updated upon return. /** A worker for ohciUnlinkTds(). */ Log((
"ohciUnlinkIsocTdInList: Unlinking non-head ITD! TdAddr=%#010RX32 HeadTdAddr=%#010RX32 LastEdAddr=%#010RX32\n",
Log((
"ohciUnlinkIsocTdInList: TdAddr=%#010RX32 wasn't found in the list!!! (cMax=%d)\n",
TdAddr,
cMax));
/** A worker for ohciUnlinkTds(). */ Log((
"ohciUnlinkGeneralTdInList: Unlinking non-head TD! TdAddr=%#010RX32 HeadTdAddr=%#010RX32 LastEdAddr=%#010RX32\n",
Log((
"ohciUnlinkGeneralTdInList: TdAddr=%#010RX32 wasn't found in the list!!! (cMax=%d)\n",
TdAddr,
cMax));
* 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. * Unlink the TD from the ED list. * The normal case is that it's at the head of the list. * It's proably somewhere in the list, not a unlikely situation with * the current isochronous code. /** @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 /* update toggle and set data toggle carry */ if ( !!(
pEd->
HeadP &
ED_HEAD_CARRY) )
/** @todo r=bird: is it just me or doesn't this make sense at all? */ * Unlink the TD from the ED list. * The normal case is that it's at the head of the list. * 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... * Only unlink the first TD on error. * See comment in ohciRhXferCompleteGeneralURB(). * 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 pOhci The OHCI instance. * @param pUrb The URB in question. * @param pEd The ED pointer (optional). * Make sure we've got an endpoint descriptor so we can Log((
"%s: ohciHasUrbBeenCanceled: iTd=%d cTds=%d TdAddr=%#010RX32 canceled (tail)! [iso]\n",
Log((
"%s: ohciHasUrbBeenCanceled: iTd=%d cTds=%d TdAddr=%#010RX32 canceled! [iso]\n",
Log((
"%s: ohciHasUrbBeenCanceled: iTd=%d cTds=%d TdAddr=%#010RX32 canceled (tail)!\n",
Log((
"%s: ohciHasUrbBeenCanceled: iTd=%d cTds=%d TdAddr=%#010RX32 canceled!\n",
* Returns the OHCI_CC_* corresponding to the VUSB status code. * @returns OHCI_CC_* value. * @param enmStatus The VUSB status code. * 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. 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. * 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++)
/* It should already be NotAccessed. */ pITd->
aPSW[i] |=
0xe000;
/* (Don't touch the 12th bit.) */ /* Update the PSW (save the offset first in case of a IN). */ const unsigned cb0 =
0x1000 -
off;
else /* only in the 2nd page */ else /* only in the 1st page */ Log5((
"packet %d: off=%#x cb=%#x pb=%p (%#x)\n" * If the last package ended with a NotAccessed status, set ITD CC * to DataOverrun to indicate scheduling overrun. Log((
"DevOHCI: Taking untested code path at line %d...\n",
__LINE__));
* 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 //if (pUrb->enmStatus != VUSBSTATUS_DATA_OVERRUN) for (
unsigned i = 0; i <
cFrames; i++)
// pITd->HwInfo |= ohciVUsbStatus2OhciStatus(pUrb->enmStatus); * Update the done queue interrupt timer. DoneInt = 0;
/* It's cleared on error. */ * Move on to the done list and write back the modified TD. 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",
* 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. /* (len may have changed for short transfers) */ /* advance the data buffer. */ /* zero out writeback fields for retirement */ /* always update the CurrentBufferPointer; essential for underrun/overrun errors */ /* update done queue interrupt timer */ Log((
"%s: ohciRhXferCompleteGeneralURB: ED=%#010x TD=%#010x Age=%d cbTotal=%#x NewCbp=%#010RX32 dqic=%d\n",
Log((
"%s: ohciRhXferCompleteGeneralURB: HALTED ED=%#010x TD=%#010x (age %d) pUrb->enmStatus=%d\n",
pOhci->
dqic = 0;
/* "If the Transfer Descriptor is being retired with an error, * then the Done Queue Interrupt Counter is cleared as if the * InterruptDelay field were zero." default:
/* what the hell */ * Move on to the done list and write back the modified TD. * 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 #1646.) * 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. * We drop the URB if the ED is marked halted/skip ASSUMING that this * 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. Log((
"%s: ohciRhXferCompletion: DROPPED {ED=%#010x cTds=%d TD0=%#010x age %d} because:%s%s%s%s%s!!!\n",
cFmAge < 0 ?
" td not-in-flight" :
"",
* Complete the TD updating and write the back. * When appropirate also copy data back to the guest memory. /* finaly write back the endpoint descriptor. */ * Handle transfer errors. * VUSB calls this when a transfer attempt failed. This function will respond * indicating wheter 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. * 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!!! */ * Get and update the error counter. Log2((
"%s: ohciRhXferError: too many errors, giving up!\n",
pUrb->
pszDesc));
* Service a general transport descriptor. * Read the TD and setup the buffer data. * Determin the direction. /* TODO: Do the correct thing here */ * Allocate and initialize a new URB. return false;
/* retry later... */ /* copy data if out bound transfer. */ Log((
"%s: ohciServiceTd: submitting TdAddr=%#010x EdAddr=%#010x cbData=%#x\n",
/* 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",
* Service a the head TD of an endpoint. * Read the TD, after first checking if it's already in-flight. * Service one or more general transport descriptors (bulk or interrupt). * Read the TDs involved in this URB. /** The associated OHCI buffer tracker. */ /** Pointer to the next element in the chain (stack). */ /* combine with more TDs. */ /* don't combine if the direction doesn't match up. */ /* calc next TD address */ * Determin the direction. Log((
"ohciServiceTdMultiple: WARNING! Ed.hwdinfo=%#x bulk or interrupt EP shouldn't rely on the TD for direction...\n",
pEd->
hwinfo));
/* TODO: Do the correct thing here */ * Allocate and initialize a new URB. /* Copy data and TD information. */ Log((
"%s: ohciServiceTdMultiple: submitting cbData=%#x EdAddr=%#010x cTds=%d TdAddr0=%#010x\n",
/* 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",
* Service the head TD of an endpoint. * First, check that it's not already in-flight. * A worker for ohciServiceIsochronousEndpoint which unlinks a ITD * that belongs to the past. LogFlow((
"%s%sohciServiceIsochronousTdUnlink: Unlinking ITD: ITdAddr=%#010x EdAddr=%#010x ITdAddrPrev=%#010x\n",
/* Get validate the previous TD */ /* Update the copy and write it back. */ /* 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 (?). * 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. * Determine the endpoint direction. Log((
"ohciServiceIsochronousTd: Invalid direction!!!! Ed.hwdinfo=%#x\n",
pEd->
hwinfo));
/* Should probably raise an unrecoverable HC error here */ * Extract the packet sizes and calc the total URB size. Log((
"ITdAddr=%RX32 PSW%d.offset=%#x < offPrev=%#x!\n",
ITdAddr,
iR,
off,
offPrev));
/* => Unrecoverable Error*/ /* calc offEnd and figure out the size of the last packet. */ +
1 /* BE is inclusive */;
* Allocate and initialize a new URB. #
if 0
/* color the data */ const unsigned cb0 =
0x1000 -
off0;
else /* a portion of the 1st page. */ else /* a portion of the 2nd page. */ Log((
"%s: ohciServiceIsochronousTd: submitting cbData=%#x cIsocPkts=%d EdAddr=%#010x TdAddr=%#010x SF=%#x (%#x)\n",
/* 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",
* Service an isochronous endpoint. * We currently process this as if the guest follows the interrupt end point chaining * hiearchy 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 * 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). /* check for end-of-chain. */ * 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. * 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) */ * 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 NotAccesed 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, Log((
"ohciServiceIsochronousEndpoint: Failed unlinking old ITD.\n"));
* 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 successfull with the first URB but then it goes too slowly... Log((
"ohciServiceIsochronousEndpoint: Failed unlinking old ITD.\n"));
/* 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. * 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). Log((
"ohciServiceBulkList: bulk_cur=%#010x before listprocessing!!! HCD have positioned us!!!\n",
pOhci->
bulk_cur));
* ", 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. * After we figured out that all the TDs submitted for dealing with MSD * read/write data really makes up on single URB, and that we must * reassemble these TDs into an URB before submitting it, there is no * longer any need for servicing anything other than the head *URB* * This alternative code was used before we started reassembling URBs from * multiple TDs. We keep it handy for debugging. LogFlow((
"ohciServiceBulkList: ohciServiceTdMultiple -> false\n"));
* 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). Log((
"ohciServiceCtrlList: ctrl_cur=%010x before list processing!!! HCD have positioned us!!!\n",
pOhci->
ctrl_cur));
* ", 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. * 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-fligth * or reach the end of the list. /* Simplistic, for debugging. */ * 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 * Read the list head from the HCCA. * Iterate the endpoint list. * "There is no separate head pointer of isochronous transfers. The first * isochronous Endpoint Descriptor simply links to the last interrupt * Presently we will only process the head URB on an interrupt endpoint. * Presently only the head ITD. * @param pOhci The OHCI instance data. Log((
"ohci: Writeback Done (%#010x) on frame %#x (age %#x)\n",
hcca.
done,
* Calculate frame timer variables given a frame rate (1,000 Hz is the full speed). * Generate a Start-Of-Frame event, and set a timer for End-Of-Frame. * Update HcFmRemaining.FRT and re-arm the timer. #
if 1 /* This is required for making the quickcam work on the mac. Should really look into that adaptive polling stuff... */ * 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 0
/* moved down for higher speed. */ * Should be done after SOF but before HC read first ED in this frame. /* "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. */ Log((
"ohciStartOfFrame: skipping hcca part because hcca=%RX32 (our 'valid' range: %RX32-%RX32)\n",
* 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 * back immediately. The idea is to be able to retire the data and/or status stages * 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 Log2((
"ohciStartOfFrame: HcCommandStatus=%#010x: %sHCR=%d %sCLF=%d %sBLF=%d %sOCR=%d %sSOC=%d\n",
chg & (
3<<
16)?
"*" :
"", (
val >>
16) &
3));
* Adjust the frame timer interval based on idle detection. /* Set the new frame rate based on how long we've been idle. Tunable. */ /* If we're switching back to full speed, re-program the timer immediately to minimize latency. */ * Updates the HcFmNumber and FNO registers. * Do frame processing on frame boundary /* Reset idle detection flag */ /* Frame boundary, so do EOF stuf here */ /* Start the next frame */ * Start sending SOF tokens across the USB bus, lists are processed in pOhci->
fIdle =
false;
/* Assume we won't be idle */ * Stop sending SOF tokens on the bus * Move in to resume state Log((
"pOhci: ohciBusResume fHardware=%RTbool RWE=%s\n",
/* Power a port up or down */ * Read the HcRevision register. Log2((
"HcRevision_r() -> 0x10\n"));
*
pu32Value =
0x10;
/* OHCI revision 1.0, no emulation. */ * Write to the HcRevision register. * 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 >>
9) &
1, (
ctl >>
10) &
1));
* Write the HcControl register. Log2((
"HcControl_w(%#010x) => %sCBSR=%d %sPLE=%d %sIE=%d %sCLE=%d %sBLE=%d %sHCFS=%#x %sIR=%d %sRWC=%d %sRWE=%d\n",
chg & (
3 <<
6)?
"*" :
"", (
val >>
6) &
3,
Log2((
"Unknown bits %#x are set!!!\n",
val & ~
0x07ff));
/* see what changed and take action on that. */ LogRel((
"OHCI: USB Operational\n"));
LogRel((
"OHCI: USB Suspended\n"));
LogRel((
"OHCI: USB Resume\n"));
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. */ Log2((
"HcControl_w: state changed -> VINF_IOM_HC_MMIO_WRITE\n"));
* Read the HcCommandStatus register. Log2((
"HcCommandStatus_r() -> %#010x - HCR=%d CLF=%d BLF=%d OCR=%d SOC=%d\n",
* Write to the HcCommandStatus register. Log2((
"HcCommandStatus_w(%#010x) => %sHCR=%d %sCLF=%d %sBLF=%d %sOCR=%d %sSOC=%d\n",
Log2((
"Unknown bits %#x are set!!!\n",
val & ~
0x0003000f));
/* "bits written as '0' remain unchanged in the register" */ LogRel((
"OHCI: Software reset\n"));
LogFlow((
"HcCommandStatus_w: reset -> VINF_IOM_HC_MMIO_WRITE\n"));
* 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",
(
val >>
6) &
1, (
val >>
30) &
1));
* 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 !=
0xffffffff /* ignore clear-all-like requests from xp. */)
Log2((
"Unknown bits %#x are set!!!\n",
val & ~
0xc000007f));
/* "The Host Controller Driver may clear specific bits in this * register by writing '1' to bit positions to be cleared" * 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",
(
val >>
6) &
1, (
val >>
30) &
1, (
val >>
31) &
1));
* 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",
Log2((
"Uknown bits %#x are set!!!\n",
val & ~
0xc000007f));
* Reads the HcInterruptDisable register. #
if 1 /** @todo r=bird: "On read, the current value of the HcInterruptEnable register is returned." */ Log2((
"HcInterruptDisable_r() -> %#010x - SO=%d WDH=%d SF=%d RD=%d UE=%d FNO=%d RHSC=%d OC=%d MIE=%d\n",
(
val >>
6) &
1, (
val >>
30) &
1, (
val >>
31) &
1));
* 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",
/* Don't bitch about invalid bits here since it makes sense to disble * interrupts you don't know about. */ * Read the HcHCCA register (Host Controller Communications Area physical address). * Write to the HcHCCA register (Host Controller Communications Area physical address). * Read the HcPeriodCurrentED register. * 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, pOhci->per_cur)); * Read the HcControlHeadED register. * Write to the HcControlHeadED register. * Read the HcControlCurrentED register. * Write to the HcControlCurrentED register. * Read the HcBulkHeadED register. * Write to the HcBulkHeadED register. * Read the HcBulkCurrentED register. * Write to the HcBulkCurrentED register. * Read the HcDoneHead register. * Write to the HcDoneHead register. Log2((
"HcDoneHead_w(0x%#08x) - denied!!!\n",
val));
* Read the HcFmInterval (Fm=Frame) register. Log2((
"HcFmInterval_r() -> 0x%#08x - FI=%d FSMPS=%d FIT=%d\n",
* Write to the HcFmInterval (Fm = Frame) register. Log2((
"HcFmInterval_w(%#010x) => %sFI=%d %sFSMPS=%d %sFIT=%d\n",
chg &
0x00003fff ?
"*" :
"",
val &
0x3fff,
chg &
0x7fff0000 ?
"*" :
"", (
val >>
16) &
0x7fff,
chg >>
31 ?
"*" :
"", (
val >>
31) &
1));
* Read the HcFmRemaining (Fm = Frame) register. * Being in USB operational state guarantees SofTime was set already. * Write to the HcFmRemaining (Fm = Frame) register. Log2((
"HcFmRemaining_w(%#010x) - denied\n",
val));
* Read the HcFmNumber (Fm = Frame) register. * Write to the HcFmNumber (Fm = Frame) register. Log2((
"HcFmNumber_w(%#010x) - denied\n",
val));
* Read the HcPeriodicStart register. * The register determins when in a frame to switch from control&bulk to periodic lists. * Write to the HcPeriodicStart register. * The register determins when in a frame to switch from control&bulk to periodic lists. Log2((
"HcPeriodicStart_w(%#010x) => PS=%d\n",
val,
val &
0x3fff));
Log2((
"Unknown bits %#x are set!!!\n",
val & ~
0x3fff));
pOhci->
pstart =
val;
/** @todo r=bird: should we support setting the other bits? */ * Read the HcLSThreshold register. * 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. Log2((
"HcLSThreshold_w(%#010x) => LST=0x%03x(%d)\n",
val,
val &
0x0fff,
val &
0x0fff));
(
"HCD tried to write bad LS threshold: 0x%x (see function header)\n",
val));
* Read the HcRhDescriptorA register. Log2((
"HcRhDescriptorA_r() -> %#010x - NDP=%d PSM=%d NPS=%d DT=%d OCPM=%d NOCP=%d POTGT=%#x\n",
(
val >>
12) &
1, (
val >>
24) &
0xff));
* 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",
(
chg >>
8) &
1 ?
"*" :
"", (
val >>
8) &
1,
(
chg >>
9) &
1 ?
"*" :
"", (
val >>
9) &
1,
(
chg >>
10) &
1 ?
"!!!":
"", 0,
(
chg >>
11) &
1 ?
"*" :
"", (
val >>
11) &
1,
(
chg >>
12) &
1 ?
"*" :
"", (
val >>
12) &
1,
(
chg >>
24)&
0xff?
"*" :
"", (
val >>
24) &
0xff,
Log2((
"Unknown bits %#x are set!!!\n",
val & ~
0xff001fff));
Log((
"ohci: %s: invalid write to NDP or DT in roothub descriptor A!!! val=0x%.8x\n",
* Read the HcRhDescriptorB register. Log2((
"HcRhDescriptorB_r() -> %#010x - DR=0x%04x PPCM=0x%04x\n",
* Write to the HcRhDescriptorB register. Log2((
"HcRhDescriptorB_w(%#010x) => %sDR=0x%04x %sPPCM=0x%04x\n",
chg &
0xffff ?
"!!!" :
"",
val &
0xffff,
chg >>
16 ?
"!!!" :
"",
val >>
16));
Log((
"ohci: %s: unsupported write to root decriptor B!!! 0x%.8x -> 0x%.8x\n",
* Read the HcRhStatus (Rh = Root Hub) register. Log2((
"HcRhStatus_r() -> %#010x - LPS=%d OCI=%d DRWE=%d LPSC=%d OCIC=%d CRWE=%d\n",
* Write to the HcRhStatus (Rh = Root Hub) register. Log2((
"HcRhStatus_w: Unknown bits %#x are set!!!\n",
val & ~
0x80038003));
Log2((
"HcRhStatus_w: Warning both CGP and SGP are set! (Clear/Set Global Power)\n"));
Log2((
"HcRhStatus_w: Warning both CRWE and SRWE are set! (Clear/Set Remote Wakeup Enable)\n"));
/* write 1 to clear OCIC */ Log2((
"HcRhStatus_w(%#010x) => %sCGP=%d %sOCI=%d %sSRWE=%d %sSGP=%d %sOCIC=%d %sCRWE=%d\n",
(
chg >>
1) &
1 ?
"!!!":
"", (
val >>
1) &
1,
(
chg >>
15) &
1 ?
"*" :
"", (
val >>
15) &
1,
(
chg >>
16) &
1 ?
"*" :
"", (
val >>
16) &
1,
(
chg >>
17) &
1 ?
"*" :
"", (
val >>
17) &
1,
(
chg >>
31) &
1 ?
"*" :
"", (
val >>
31) &
1));
* Read the HcRhPortStatus register of a port. const unsigned i =
iReg -
21;
Log2((
"HcRhPortStatus_r: yield -> VINF_IOM_HC_MMIO_READ\n"));
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",
(
val >>
16) &
1, (
val >>
17) &
1, (
val >>
18) &
1, (
val >>
19) &
1, (
val >>
20) &
1));
* Completion callback for the vusb_dev_reset() operation. * Find the port in question Assert(
pPort);
/* sometimes happends because of #1510 */ Log2((
"uchi_port_reset_done: Reset completed.\n"));
/* desperate measures. */ * Damn, something weird happend during reset. We'll pretend the user did an * incredible fast reconnect or something. (prolly not gonna work) Log2((
"uchi_port_reset_done: The reset failed (rc=%Rrc)!!! Pretending reconnect at the speed of light.\n",
rc));
* The device have / will be disconnected. Log2((
"uchi_port_reset_done: Disconnected (rc=%Rrc)!!!\n",
rc));
/* 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 * @returns true if device was connected and the flag was cleared. * Writing a 0 has no effect * If CurrentConnectStatus is cleared we set ConnectStatusChange. * Write to the HcRhPortStatus register of a port. const unsigned i =
iReg -
21;
"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" Log2((
"HcRhPortStatus_w(%#010x): port %u:",
val, i));
/* Write to clear any of the change bits: CSC, PESC, PSSC, OCIC and PRSC */ 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));
/* 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 /** @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",
(
chg >>
1) &
1 ?
"*" :
"", (
res >>
1) &
1,
(
chg >>
2) &
1 ?
"*" :
"", (
res >>
2) &
1,
(
chg >>
3) &
1 ?
"*" :
"", (
res >>
3) &
1,
(
chg >>
4) &
1 ?
"*" :
"", (
res >>
4) &
1,
(
chg >>
8) &
1 ?
"*" :
"", (
res >>
8) &
1,
(
chg >>
9) &
1 ?
"*" :
"", (
res >>
9) &
1,
(
chg >>
16) &
1 ?
"*" :
"", (
res >>
16) &
1,
(
chg >>
17) &
1 ?
"*" :
"", (
res >>
17) &
1,
(
chg >>
18) &
1 ?
"*" :
"", (
res >>
18) &
1,
(
chg >>
19) &
1 ?
"*" :
"", (
res >>
19) &
1,
(
chg >>
20) &
1 ?
"*" :
"", (
res >>
20) &
1));
* Register descriptor table /* The number of port status register depends on the definition * We only accept 32-bit writes that are 32-bit aligned. * @returns VBox status code suitable for scheduling. * @param pDevIns The device instance. * @param pvUser A user argument (ignored). * @param GCPhysAddr The physical address being written to. (This is within our MMIO memory range.) * @param pv Where to put the data we read. * @param cb The size of the read. * Validate the register and call the read operator. * Write to a MMIO register. * We only accept 32-bit writes that are 32-bit aligned. * @returns VBox status code suitable for scheduling. * @param pDevIns The device instance. * @param pvUser A user argument (ignored). * @param GCPhysAddr The physical address being written to. (This is within our MMIO memory range.) * @param pv Pointer to the data being written. * @param cb The size of the data being written. Log2((
"ohciWrite: Unaligned write!!! GCPhysAddr=%RGp cb=%d\n",
GCPhysAddr,
cb));
* Validate the register and call the read operator. #
if 1 /* this enabled / disabled GC/R0 stuff */ * 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. * Detach all proxied devices. /** @todo we a) can't tell which are proxied, and b) this won't work well when continuing after saving! */ * 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. * 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. * 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. * 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. * Detach all devices which are present in this session. Save them in the load * structure so we can reattach them after restoring the guest. * Any devices to reattach, if so duplicate the Load struct. /* else: we ASSUME no device can be attached or detach in the periode * between a state load and the pLoad stuff is processed. */ * 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. /* deserialize the struct */ * 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. * Start a timer if we've got devices to reattach * Reattaches devices after a saved state load. LogFlow((
"ohciR3LoadReattachDevices:\n"));
* @param pDevIns The device instance data. * 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. 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 >>
9) &
1, (
ctl >>
10) &
1);
/* Command status register */ pHlp->
pfnPrintf(
pHlp,
"HcCommandStatus: %08x - HCR=%d CLF=%d BLF=%d OCR=%d SOC=%d\n",
/* 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",
(
val >>
6) &
1, (
val >>
30) &
1);
/* 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",
(
val >>
6) &
1, (
val >>
30) &
1, (
val >>
31) &
1);
/* HCCA address register */ /* Current periodic ED register */ /* Control ED registers */ * Relocate device instance data. * @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. * @param pDevIns The device instance data. * Tear down the per endpoint in-flight tracking... * @interface_method_impl{PDMDEVREG,pfnConstruct,OHCI constructor} * Read configuration. No configuration keys are currently supported. N_(
"Configuration error: Unknown config key"));
* Register PCI device and I/O region. * Create the end-of-frame timer. * Register the saved state data unit. * Attach to the VBox USB RootHub Driver on LUN #0. AssertMsgFailed((
"Configuration error: No roothub driver attached to LUN #0!\n"));
(
"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). * Calculate the timer intervals. * This assumes that the VM timer doesn't change frequency during the run. Log((
"ohci: cTicksPerFrame=%RU64 cTicksPerUsbTick=%RU64\n",
* Register debugger info callbacks. // g_fLogInterruptEPs = true; "OHCI USB controller.\n",
#
endif /* !VBOX_DEVICE_STRUCT_TESTCASE */