reactos/drivers/usb/usbehci_new/hardware.cpp
Michael Martin 9b44c5121d [USBEHCI_NEW]
- Add flags and structures needed for communicating with controller and handling schedules.
- Add support routines for modifying operational registers on controller.
- Implement getting the controller capabilties, starting and stopping the controller.

svn path=/branches/usb-bringup/; revision=51365
2011-04-16 02:20:23 +00:00

582 lines
15 KiB
C++

/*
* PROJECT: ReactOS Universal Serial Bus Bulk Enhanced Host Controller Interface
* LICENSE: GPL - See COPYING in the top level directory
* FILE: drivers/usb/usbehci/hcd_controller.cpp
* PURPOSE: USB EHCI device driver.
* PROGRAMMERS:
* Michael Martin (michael.martin@reactos.org)
* Johannes Anderwald (johannes.anderwald@reactos.org)
*/
#define INITGUID
#include "usbehci.h"
#include "hardware.h"
BOOLEAN
NTAPI
InterruptServiceRoutine(
IN PKINTERRUPT Interrupt,
IN PVOID ServiceContext);
class CUSBHardwareDevice : public IUSBHardwareDevice
{
public:
STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface);
STDMETHODIMP_(ULONG) AddRef()
{
InterlockedIncrement(&m_Ref);
return m_Ref;
}
STDMETHODIMP_(ULONG) Release()
{
InterlockedDecrement(&m_Ref);
if (!m_Ref)
{
delete this;
return 0;
}
return m_Ref;
}
// com
NTSTATUS Initialize(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT FunctionalDeviceObject, PDEVICE_OBJECT PhysicalDeviceObject, PDEVICE_OBJECT LowerDeviceObject);
NTSTATUS PnpStart(PCM_RESOURCE_LIST RawResources, PCM_RESOURCE_LIST TranslatedResources);
NTSTATUS PnpStop(void);
NTSTATUS HandlePower(PIRP Irp);
NTSTATUS GetDeviceDetails(PULONG VendorId, PULONG DeviceId, PULONG NumberOfPorts, PULONG Speed);
NTSTATUS GetDmaMemoryManager(OUT struct IDMAMemoryManager **OutMemoryManager);
NTSTATUS GetUSBQueue(OUT struct IUSBQueue **OutUsbQueue);
NTSTATUS StartController();
NTSTATUS StopController();
NTSTATUS ResetController();
NTSTATUS ResetPort(ULONG PortIndex);
KIRQL AcquireDeviceLock(void);
VOID ReleaseDeviceLock(KIRQL OldLevel);
// local
BOOLEAN InterruptService();
// friend function
friend BOOLEAN NTAPI InterruptServiceRoutine(IN PKINTERRUPT Interrupt, IN PVOID ServiceContext);
// constructor / destructor
CUSBHardwareDevice(IUnknown *OuterUnknown){}
virtual ~CUSBHardwareDevice(){}
protected:
LONG m_Ref;
PDRIVER_OBJECT m_DriverObject;
PDEVICE_OBJECT m_PhysicalDeviceObject;
PDEVICE_OBJECT m_FunctionalDeviceObject;
PDEVICE_OBJECT m_NextDeviceObject;
KSPIN_LOCK m_Lock;
PKINTERRUPT m_Interrupt;
PULONG m_Base;
PDMA_ADAPTER m_Adapter;
ULONG m_MapRegisters;
PQUEUE_HEAD AsyncListQueueHead;
EHCI_CAPS m_Capabilities;
VOID SetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd);
VOID GetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd);
VOID SetStatusRegister(PEHCI_USBSTS_CONTENT UsbSts);
VOID GetStatusRegister(PEHCI_USBSTS_CONTENT UsbSts);
//VOID SetPortRegister(PEHCI_USBPORTSC_CONTENT UsbPort);
//VOID GetPortRegister(PEHCI_USBPORTSC_CONTENT UsbPort);
ULONG EHCI_READ_REGISTER_ULONG(ULONG Offset);
ULONG EHCI_READ_REGISTER_USHORT(ULONG Offset);
ULONG EHCI_READ_REGISTER_UCHAR(ULONG Offset);
VOID EHCI_WRITE_REGISTER_ULONG(ULONG Offset, ULONG Value);
VOID EHCI_WRITE_REGISTER_USHORT(ULONG Offset, ULONG Value);
VOID EHCI_WRITE_REGISTER_UCHAR(ULONG Offset, ULONG Value);
};
//=================================================================================================
// COM
//
NTSTATUS
STDMETHODCALLTYPE
CUSBHardwareDevice::QueryInterface(
IN REFIID refiid,
OUT PVOID* Output)
{
if (IsEqualGUIDAligned(refiid, IID_IUnknown))
{
*Output = PVOID(PUNKNOWN(this));
PUNKNOWN(*Output)->AddRef();
return STATUS_SUCCESS;
}
return STATUS_UNSUCCESSFUL;
}
NTSTATUS
CUSBHardwareDevice::Initialize(
PDRIVER_OBJECT DriverObject,
PDEVICE_OBJECT FunctionalDeviceObject,
PDEVICE_OBJECT PhysicalDeviceObject,
PDEVICE_OBJECT LowerDeviceObject)
{
DPRINT1("CUSBHardwareDevice::Initialize\n");
//
// store device objects
//
m_DriverObject = DriverObject;
m_FunctionalDeviceObject = FunctionalDeviceObject;
m_PhysicalDeviceObject = PhysicalDeviceObject;
m_NextDeviceObject = LowerDeviceObject;
//
// initialize device lock
//
KeInitializeSpinLock(&m_Lock);
return STATUS_SUCCESS;
}
VOID
CUSBHardwareDevice::SetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd)
{
PULONG Register;
Register = (PULONG)UsbCmd;
WRITE_REGISTER_ULONG((PULONG)((ULONG)m_Base + EHCI_USBCMD), *Register);
}
VOID
CUSBHardwareDevice::GetCommandRegister(PEHCI_USBCMD_CONTENT UsbCmd)
{
PULONG Register;
Register = (PULONG)UsbCmd;
*Register = READ_REGISTER_ULONG((PULONG)((ULONG)m_Base + EHCI_USBCMD));
}
VOID
CUSBHardwareDevice::SetStatusRegister(PEHCI_USBSTS_CONTENT UsbSts)
{
PULONG Register;
Register = (PULONG)UsbSts;
WRITE_REGISTER_ULONG((PULONG)((ULONG)m_Base + EHCI_USBSTS), *Register);
}
VOID
CUSBHardwareDevice::GetStatusRegister(PEHCI_USBSTS_CONTENT UsbSts)
{
PULONG CmdRegister;
CmdRegister = (PULONG)UsbSts;
*CmdRegister = READ_REGISTER_ULONG((PULONG)((ULONG)m_Base + EHCI_USBSTS));
}
ULONG
CUSBHardwareDevice::EHCI_READ_REGISTER_ULONG(ULONG Offset)
{
return READ_REGISTER_ULONG((PULONG)((ULONG)m_Base + Offset));
}
ULONG
CUSBHardwareDevice::EHCI_READ_REGISTER_USHORT(ULONG Offset)
{
return READ_REGISTER_USHORT((PUSHORT)((ULONG)m_Base + Offset));
}
ULONG
CUSBHardwareDevice::EHCI_READ_REGISTER_UCHAR(ULONG Offset)
{
return READ_REGISTER_UCHAR((PUCHAR)((ULONG)m_Base + Offset));
}
VOID
CUSBHardwareDevice::EHCI_WRITE_REGISTER_ULONG(ULONG Offset, ULONG Value)
{
WRITE_REGISTER_ULONG((PULONG)((ULONG)m_Base + Offset), Value);
}
VOID
CUSBHardwareDevice::EHCI_WRITE_REGISTER_USHORT(ULONG Offset, ULONG Value)
{
WRITE_REGISTER_USHORT((PUSHORT)((ULONG)m_Base + Offset), Value);
}
VOID
CUSBHardwareDevice::EHCI_WRITE_REGISTER_UCHAR(ULONG Offset, ULONG Value)
{
WRITE_REGISTER_UCHAR((PUCHAR)((ULONG)m_Base + Offset), Value);
}
NTSTATUS
CUSBHardwareDevice::PnpStart(
PCM_RESOURCE_LIST RawResources,
PCM_RESOURCE_LIST TranslatedResources)
{
ULONG Index, Count;
PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor;
DEVICE_DESCRIPTION DeviceDescription;
PVOID ResourceBase;
NTSTATUS Status;
DPRINT1("CUSBHardwareDevice::PnpStart\n");
for(Index = 0; Index < TranslatedResources->List[0].PartialResourceList.Count; Index++)
{
//
// get resource descriptor
//
ResourceDescriptor = &TranslatedResources->List[0].PartialResourceList.PartialDescriptors[Index];
switch(ResourceDescriptor->Type)
{
case CmResourceTypeInterrupt:
{
Status = IoConnectInterrupt(&m_Interrupt,
InterruptServiceRoutine,
(PVOID)this,
NULL,
ResourceDescriptor->u.Interrupt.Vector,
(KIRQL)ResourceDescriptor->u.Interrupt.Level,
(KIRQL)ResourceDescriptor->u.Interrupt.Level,
(KINTERRUPT_MODE)(ResourceDescriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED),
(ResourceDescriptor->ShareDisposition != CmResourceShareDeviceExclusive),
ResourceDescriptor->u.Interrupt.Affinity,
FALSE);
if (!NT_SUCCESS(Status))
{
//
// failed to register interrupt
//
DPRINT1("IoConnect Interrupt failed with %x\n", Status);
return Status;
}
break;
}
case CmResourceTypeMemory:
{
//
// get resource base
//
ResourceBase = MmMapIoSpace(ResourceDescriptor->u.Memory.Start, ResourceDescriptor->u.Memory.Length, MmNonCached);
if (!ResourceBase)
{
//
// failed to map registers
//
DPRINT1("MmMapIoSpace failed\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Get controllers capabilities
//
m_Capabilities.Length = READ_REGISTER_UCHAR((PUCHAR)ResourceBase);
m_Capabilities.HCIVersion = READ_REGISTER_USHORT((PUSHORT)((ULONG)ResourceBase + 2));
m_Capabilities.HCSParamsLong = READ_REGISTER_ULONG((PULONG)((ULONG)ResourceBase + 4));
m_Capabilities.HCCParamsLong = READ_REGISTER_ULONG((PULONG)((ULONG)ResourceBase + 8));
DPRINT1("Controller has %d Ports\n", m_Capabilities.HCSParams.PortCount);
if (m_Capabilities.HCSParams.PortRouteRules)
{
for (Count = 0; Count < m_Capabilities.HCSParams.PortCount; Count++)
{
m_Capabilities.PortRoute[Count] = READ_REGISTER_UCHAR((PUCHAR)(ULONG)ResourceBase + 12 + Count);
}
}
//
// Set m_Base to the address of Operational Register Space
//
m_Base = (PULONG)((ULONG)ResourceBase + m_Capabilities.Length);
break;
}
}
}
//
// zero device description
//
RtlZeroMemory(&DeviceDescription, sizeof(DEVICE_DESCRIPTION));
//
// initialize device description
//
DeviceDescription.Version = DEVICE_DESCRIPTION_VERSION;
DeviceDescription.Master = TRUE;
DeviceDescription.ScatterGather = TRUE;
DeviceDescription.Dma32BitAddresses = TRUE;
DeviceDescription.DmaWidth = Width32Bits;
DeviceDescription.InterfaceType = PCIBus;
DeviceDescription.MaximumLength = MAXULONG;
//
// get dma adapter
//
m_Adapter = IoGetDmaAdapter(m_PhysicalDeviceObject, &DeviceDescription, &m_MapRegisters);
if (!m_Adapter)
{
//
// failed to get dma adapter
//
DPRINT1("Failed to acquire dma adapter\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// FIXME: Create a QueueHead that will always be the address of the AsyncList
//
AsyncListQueueHead = NULL;
//
// Start the controller
//
DPRINT1("Starting Controller\n");
return StartController();
}
NTSTATUS
CUSBHardwareDevice::PnpStop(void)
{
UNIMPLEMENTED
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
CUSBHardwareDevice::HandlePower(
PIRP Irp)
{
UNIMPLEMENTED
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
CUSBHardwareDevice::GetDeviceDetails(
OUT OPTIONAL PULONG VendorId,
OUT OPTIONAL PULONG DeviceId,
OUT OPTIONAL PULONG NumberOfPorts,
OUT OPTIONAL PULONG Speed)
{
UNIMPLEMENTED
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
CUSBHardwareDevice::GetDmaMemoryManager(
OUT struct IDMAMemoryManager **OutMemoryManager)
{
UNIMPLEMENTED
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
CUSBHardwareDevice::GetUSBQueue(
OUT struct IUSBQueue **OutUsbQueue)
{
UNIMPLEMENTED
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
CUSBHardwareDevice::StartController(void)
{
EHCI_USBCMD_CONTENT UsbCmd;
EHCI_USBSTS_CONTENT UsbSts;
LONG FailSafe;
//
// Stop the controller if its running
//
GetStatusRegister(&UsbSts);
if (UsbSts.HCHalted)
StopController();
//
// Reset the device. Bit is set to 0 on completion.
//
SetCommandRegister(&UsbCmd);
UsbCmd.HCReset = TRUE;
SetCommandRegister(&UsbCmd);
//
// Check that the controller reset
//
for (FailSafe = 100; FailSafe > 1; FailSafe--)
{
KeStallExecutionProcessor(10);
GetCommandRegister(&UsbCmd);
if (!UsbCmd.HCReset)
{
break;
}
}
//
// If the controller did not reset then fail
//
if (UsbCmd.HCReset)
{
DPRINT1("EHCI ERROR: Controller failed to reset. Hardware problem!\n");
return STATUS_UNSUCCESSFUL;
}
//
// Disable Interrupts and clear status
//
EHCI_WRITE_REGISTER_ULONG(EHCI_USBINTR, 0);
EHCI_WRITE_REGISTER_ULONG(EHCI_USBSTS, 0x0000001f);
//
// FIXME: Assign the AsyncList Register
//
//
// Set Schedules to Enable and Interrupt Threshold to 1ms.
//
GetCommandRegister(&UsbCmd);
UsbCmd.PeriodicEnable = FALSE;
UsbCmd.AsyncEnable = FALSE; //FIXME: Need USB Memory Manager
UsbCmd.IntThreshold = 1;
// FIXME: Set framlistsize when periodic is implemented.
SetCommandRegister(&UsbCmd);
//
// Enable Interrupts and start execution
//
EHCI_WRITE_REGISTER_ULONG(EHCI_USBINTR, EHCI_USBINTR_INTE | EHCI_USBINTR_ERR | EHCI_USBINTR_ASYNC | EHCI_USBINTR_HSERR
/*| EHCI_USBINTR_FLROVR*/ | EHCI_USBINTR_PC);
UsbCmd.Run = TRUE;
SetCommandRegister(&UsbCmd);
//
// Wait for execution to start
//
for (FailSafe = 100; FailSafe > 1; FailSafe--)
{
KeStallExecutionProcessor(10);
GetStatusRegister(&UsbSts);
if (!UsbSts.HCHalted)
{
break;
}
}
if (!UsbSts.HCHalted)
{
DPRINT1("Could not start execution on the controller\n");
return STATUS_UNSUCCESSFUL;
}
//
// Set port routing to EHCI controller
//
EHCI_WRITE_REGISTER_ULONG(EHCI_CONFIGFLAG, 1);
DPRINT1("EHCI Started!\n");
return STATUS_SUCCESS;
}
NTSTATUS
CUSBHardwareDevice::StopController(void)
{
EHCI_USBCMD_CONTENT UsbCmd;
EHCI_USBSTS_CONTENT UsbSts;
LONG FailSafe;
//
// Disable Interrupts and stop execution
EHCI_WRITE_REGISTER_ULONG (EHCI_USBINTR, 0);
GetCommandRegister(&UsbCmd);
UsbCmd.Run = FALSE;
SetCommandRegister(&UsbCmd);
for (FailSafe = 100; FailSafe > 1; FailSafe--)
{
KeStallExecutionProcessor(10);
GetStatusRegister(&UsbSts);
if (UsbSts.HCHalted)
{
break;
}
}
if (!UsbSts.HCHalted)
{
DPRINT1("EHCI ERROR: Controller is not responding to Stop request!\n");
return STATUS_UNSUCCESSFUL;
}
return STATUS_SUCCESS;
}
NTSTATUS
CUSBHardwareDevice::ResetController(void)
{
UNIMPLEMENTED
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
CUSBHardwareDevice::ResetPort(
IN ULONG PortIndex)
{
UNIMPLEMENTED
return STATUS_NOT_IMPLEMENTED;
}
KIRQL
CUSBHardwareDevice::AcquireDeviceLock(void)
{
KIRQL OldLevel;
//
// acquire lock
//
KeAcquireSpinLock(&m_Lock, &OldLevel);
//
// return old irql
//
return OldLevel;
}
VOID
CUSBHardwareDevice::ReleaseDeviceLock(
KIRQL OldLevel)
{
KeReleaseSpinLock(&m_Lock, OldLevel);
}
BOOLEAN
NTAPI
InterruptServiceRoutine(
IN PKINTERRUPT Interrupt,
IN PVOID ServiceContext)
{
UNIMPLEMENTED
return FALSE;
}
NTSTATUS
CreateUSBHardware(
PUSBHARDWAREDEVICE *OutHardware)
{
PUSBHARDWAREDEVICE This;
This = new(NonPagedPool, TAG_USBEHCI) CUSBHardwareDevice(0);
if (!This)
return STATUS_INSUFFICIENT_RESOURCES;
This->AddRef();
// return result
*OutHardware = (PUSBHARDWAREDEVICE)This;
return STATUS_SUCCESS;
}