mirror of
https://github.com/reactos/reactos.git
synced 2024-08-31 15:55:41 +00:00
![Hermès Bélusca-Maïto](/assets/img/avatar_default.png)
The idea then would be to have the following behaviour (when specifying the following options in the kernel command line): /DEBUGPORT=COMi --> load KDCOM.DLL and use COMi port (i == 1,2,3,4) if possible. /DEBUGPORT=FOO --> load KDFOO.DLL (useful for KDUSB.DLL, KD1394.DLL, KDBAZIS.DLL for VirtualKD, etc...) /DEBUGPORT=ROSDBG:[COMi|SCREEN|FILE|GDB|...] --> load KDROSDBG.DLL which contains the ROS kernel debugger, and use COMi or SCREEN or... as output port. svn path=/branches/kd++/; revision=58883
1516 lines
38 KiB
C++
1516 lines
38 KiB
C++
/*
|
|
* PROJECT: ReactOS Universal Serial Bus Host Controller Interface
|
|
* LICENSE: GPL - See COPYING in the top level directory
|
|
* FILE: drivers/usb/usbuhci/hcd_controller.cpp
|
|
* PURPOSE: USB UHCI device driver.
|
|
* PROGRAMMERS:
|
|
* Michael Martin (michael.martin@reactos.org)
|
|
* Johannes Anderwald (johannes.anderwald@reactos.org)
|
|
*/
|
|
|
|
#define INITGUID
|
|
#include "usbuhci.h"
|
|
#include "hardware.h"
|
|
|
|
typedef VOID __stdcall HD_INIT_CALLBACK(IN PVOID CallBackContext);
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
InterruptServiceRoutine(
|
|
IN PKINTERRUPT Interrupt,
|
|
IN PVOID ServiceContext);
|
|
|
|
VOID
|
|
NTAPI
|
|
UhciDefferedRoutine(
|
|
IN PKDPC Dpc,
|
|
IN PVOID DeferredContext,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2);
|
|
|
|
VOID
|
|
NTAPI
|
|
TimerDpcRoutine(
|
|
IN PKDPC Dpc,
|
|
IN PVOID DeferredContext,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2);
|
|
|
|
|
|
VOID
|
|
NTAPI
|
|
StatusChangeWorkItemRoutine(PVOID Context);
|
|
|
|
|
|
|
|
class CUSBHardwareDevice : public IUHCIHardwareDevice
|
|
{
|
|
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
|
|
IMP_IUSBHARDWAREDEVICE
|
|
IMP_IUHCIHARDWAREDEVICE
|
|
|
|
// local
|
|
NTSTATUS StartController();
|
|
NTSTATUS StopController();
|
|
NTSTATUS ResetController();
|
|
VOID GlobalReset();
|
|
BOOLEAN InterruptService();
|
|
NTSTATUS InitializeController();
|
|
|
|
// friend function
|
|
friend BOOLEAN NTAPI InterruptServiceRoutine(IN PKINTERRUPT Interrupt, IN PVOID ServiceContext);
|
|
friend VOID NTAPI UhciDefferedRoutine(IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2);
|
|
friend VOID NTAPI TimerDpcRoutine(IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2);
|
|
friend VOID NTAPI StatusChangeWorkItemRoutine(PVOID Context);
|
|
VOID WriteRegister8(IN ULONG Register, IN UCHAR value);
|
|
VOID WriteRegister16(ULONG Register, USHORT Value);
|
|
VOID WriteRegister32(ULONG Register, ULONG Value);
|
|
UCHAR ReadRegister8(ULONG Register);
|
|
USHORT ReadRegister16(ULONG Register);
|
|
ULONG ReadRegister32(ULONG Register);
|
|
|
|
// constructor / destructor
|
|
CUSBHardwareDevice(IUnknown *OuterUnknown){}
|
|
virtual ~CUSBHardwareDevice(){}
|
|
|
|
protected:
|
|
LONG m_Ref; // reference count
|
|
PDRIVER_OBJECT m_DriverObject; // driver object
|
|
PDEVICE_OBJECT m_PhysicalDeviceObject; // pdo
|
|
PDEVICE_OBJECT m_FunctionalDeviceObject; // fdo (hcd controller)
|
|
PDEVICE_OBJECT m_NextDeviceObject; // lower device object
|
|
KSPIN_LOCK m_Lock; // hardware lock
|
|
PKINTERRUPT m_Interrupt; // interrupt object
|
|
KDPC m_IntDpcObject; // dpc object for deferred isr processing
|
|
PVOID VirtualBase; // virtual base for memory manager
|
|
PHYSICAL_ADDRESS PhysicalAddress; // physical base for memory manager
|
|
PULONG m_Base; // UHCI operational port base registers
|
|
PDMA_ADAPTER m_Adapter; // dma adapter object
|
|
ULONG m_MapRegisters; // map registers count
|
|
USHORT m_VendorID; // vendor id
|
|
USHORT m_DeviceID; // device id
|
|
PUHCIQUEUE m_UsbQueue; // usb request queue
|
|
ULONG m_NumberOfPorts; // number of ports
|
|
PDMAMEMORYMANAGER m_MemoryManager; // memory manager
|
|
HD_INIT_CALLBACK* m_SCECallBack; // status change callback routine
|
|
PVOID m_SCEContext; // status change callback routine context
|
|
//WORK_QUEUE_ITEM m_StatusChangeWorkItem; // work item for status change callback
|
|
ULONG m_InterruptMask; // interrupt enabled mask
|
|
ULONG m_PortResetChange; // port reset status
|
|
PULONG m_FrameList; // frame list
|
|
PHYSICAL_ADDRESS m_FrameListPhysicalAddress; // frame list physical address
|
|
PUSHORT m_FrameBandwidth; // frame bandwidth
|
|
PUHCI_QUEUE_HEAD m_QueueHead[5]; // queue heads
|
|
PHYSICAL_ADDRESS m_StrayDescriptorPhysicalAddress; // physical address stray descriptor
|
|
PUHCI_TRANSFER_DESCRIPTOR m_StrayDescriptor; // stray descriptor
|
|
KTIMER m_SCETimer; // SCE timer
|
|
KDPC m_SCETimerDpc; // timer dpc
|
|
};
|
|
|
|
//=================================================================================================
|
|
// 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;
|
|
}
|
|
|
|
LPCSTR
|
|
STDMETHODCALLTYPE
|
|
CUSBHardwareDevice::GetUSBType()
|
|
{
|
|
return "USBUHCI";
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CUSBHardwareDevice::Initialize(
|
|
PDRIVER_OBJECT DriverObject,
|
|
PDEVICE_OBJECT FunctionalDeviceObject,
|
|
PDEVICE_OBJECT PhysicalDeviceObject,
|
|
PDEVICE_OBJECT LowerDeviceObject)
|
|
{
|
|
BUS_INTERFACE_STANDARD BusInterface;
|
|
PCI_COMMON_CONFIG PciConfig;
|
|
NTSTATUS Status;
|
|
ULONG BytesRead;
|
|
|
|
DPRINT1("CUSBHardwareDevice::Initialize\n");
|
|
|
|
//
|
|
// Create DMAMemoryManager for use with QueueHeads and Transfer Descriptors.
|
|
//
|
|
Status = CreateDMAMemoryManager(&m_MemoryManager);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to create DMAMemoryManager Object\n");
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Create the UsbQueue class that will handle the Asynchronous and Periodic Schedules
|
|
//
|
|
Status = CreateUSBQueue((PUSBQUEUE*)&m_UsbQueue);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to create UsbQueue!\n");
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// store device objects
|
|
//
|
|
m_DriverObject = DriverObject;
|
|
m_FunctionalDeviceObject = FunctionalDeviceObject;
|
|
m_PhysicalDeviceObject = PhysicalDeviceObject;
|
|
m_NextDeviceObject = LowerDeviceObject;
|
|
|
|
//
|
|
// initialize device lock
|
|
//
|
|
KeInitializeSpinLock(&m_Lock);
|
|
|
|
//
|
|
// intialize status change work item
|
|
//
|
|
//ExInitializeWorkItem(&m_StatusChangeWorkItem, StatusChangeWorkItemRoutine, PVOID(this));
|
|
|
|
|
|
// initialize timer
|
|
KeInitializeTimer(&m_SCETimer);
|
|
|
|
// initialize timer dpc
|
|
KeInitializeDpc(&m_SCETimerDpc, TimerDpcRoutine, PVOID(this));
|
|
|
|
|
|
m_VendorID = 0;
|
|
m_DeviceID = 0;
|
|
|
|
Status = GetBusInterface(PhysicalDeviceObject, &BusInterface);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to get BusInteface!\n");
|
|
return Status;
|
|
}
|
|
|
|
BytesRead = (*BusInterface.GetBusData)(BusInterface.Context,
|
|
PCI_WHICHSPACE_CONFIG,
|
|
&PciConfig,
|
|
0,
|
|
PCI_COMMON_HDR_LENGTH);
|
|
|
|
if (BytesRead != PCI_COMMON_HDR_LENGTH)
|
|
{
|
|
DPRINT1("Failed to get pci config information!\n");
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
m_VendorID = PciConfig.VendorID;
|
|
m_DeviceID = PciConfig.DeviceID;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
CUSBHardwareDevice::PnpStart(
|
|
PCM_RESOURCE_LIST RawResources,
|
|
PCM_RESOURCE_LIST TranslatedResources)
|
|
{
|
|
ULONG Index;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor;
|
|
DEVICE_DESCRIPTION DeviceDescription;
|
|
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:
|
|
{
|
|
KeInitializeDpc(&m_IntDpcObject,
|
|
UhciDefferedRoutine,
|
|
this);
|
|
|
|
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 CmResourceTypePort:
|
|
{
|
|
//
|
|
// Store Resource base
|
|
//
|
|
m_Base = (PULONG)ResourceDescriptor->u.Port.Start.LowPart; //FIXME
|
|
DPRINT1("UHCI Base %p Length %x\n", m_Base, ResourceDescriptor->u.Port.Length);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
ASSERT(m_Base);
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
//
|
|
// Create Common Buffer
|
|
//
|
|
VirtualBase = m_Adapter->DmaOperations->AllocateCommonBuffer(m_Adapter,
|
|
PAGE_SIZE * 4,
|
|
&PhysicalAddress,
|
|
FALSE);
|
|
if (!VirtualBase)
|
|
{
|
|
DPRINT1("Failed to allocate a common buffer\n");
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Initialize the DMAMemoryManager
|
|
//
|
|
Status = m_MemoryManager->Initialize(this, &m_Lock, PAGE_SIZE * 4, VirtualBase, PhysicalAddress, 32);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to initialize the DMAMemoryManager\n");
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// initializes the controller
|
|
//
|
|
Status = InitializeController();
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to Initialize the controller \n");
|
|
ASSERT(FALSE);
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Initialize the UsbQueue now that we have an AdapterObject.
|
|
//
|
|
Status = m_UsbQueue->Initialize(PUSBHARDWAREDEVICE(this), m_Adapter, m_MemoryManager, NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to Initialize the UsbQueue\n");
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Start the controller
|
|
//
|
|
DPRINT1("Starting Controller\n");
|
|
Status = StartController();
|
|
|
|
|
|
//
|
|
// done
|
|
//
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
CUSBHardwareDevice::PnpStop(void)
|
|
{
|
|
UNIMPLEMENTED
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NTSTATUS
|
|
CUSBHardwareDevice::GetDeviceDetails(
|
|
OUT OPTIONAL PUSHORT VendorId,
|
|
OUT OPTIONAL PUSHORT DeviceId,
|
|
OUT OPTIONAL PULONG NumberOfPorts,
|
|
OUT OPTIONAL PULONG Speed)
|
|
{
|
|
if (VendorId)
|
|
{
|
|
//
|
|
// get vendor
|
|
//
|
|
*VendorId = m_VendorID;
|
|
}
|
|
|
|
if (DeviceId)
|
|
{
|
|
//
|
|
// get device id
|
|
//
|
|
*DeviceId = m_DeviceID;
|
|
}
|
|
|
|
if (NumberOfPorts)
|
|
{
|
|
//
|
|
// get number of ports
|
|
//
|
|
*NumberOfPorts = m_NumberOfPorts;
|
|
}
|
|
|
|
if (Speed)
|
|
{
|
|
//
|
|
// speed is 0x100
|
|
//
|
|
*Speed = 0x100;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS CUSBHardwareDevice::GetDMA(
|
|
OUT struct IDMAMemoryManager **OutDMAMemoryManager)
|
|
{
|
|
if (!m_MemoryManager)
|
|
return STATUS_UNSUCCESSFUL;
|
|
*OutDMAMemoryManager = m_MemoryManager;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
CUSBHardwareDevice::GetUSBQueue(
|
|
OUT struct IUSBQueue **OutUsbQueue)
|
|
{
|
|
if (!m_UsbQueue)
|
|
return STATUS_UNSUCCESSFUL;
|
|
*OutUsbQueue = m_UsbQueue;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CUSBHardwareDevice::StartController(void)
|
|
{
|
|
ULONG Index;
|
|
USHORT Status;
|
|
|
|
|
|
//
|
|
// debug info
|
|
//
|
|
DPRINT1("[USBUHCI] USBCMD: %x USBSTS %x\n", ReadRegister16(UHCI_USBCMD), ReadRegister16(UHCI_USBSTS));
|
|
|
|
//
|
|
// Set the run bit in the command register
|
|
//
|
|
WriteRegister16(UHCI_USBCMD, ReadRegister16(UHCI_USBCMD) | UHCI_USBCMD_RS);
|
|
|
|
for(Index = 0; Index < 100; Index++)
|
|
{
|
|
//
|
|
// wait a bit
|
|
//
|
|
KeStallExecutionProcessor(100);
|
|
|
|
//
|
|
// get controller status
|
|
//
|
|
Status = ReadRegister16(UHCI_USBSTS);
|
|
DPRINT1("[USBUHCI] Status %x\n", Status);
|
|
|
|
if (!(Status & UHCI_USBSTS_HCHALT))
|
|
{
|
|
//
|
|
// controller started
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
|
|
DPRINT1("[USBUHCI] USBCMD: %x USBSTS %x\n", ReadRegister16(UHCI_USBCMD), ReadRegister16(UHCI_USBSTS));
|
|
|
|
|
|
if ((Status & UHCI_USBSTS_HCHALT))
|
|
{
|
|
//
|
|
// failed to start controller
|
|
//
|
|
DPRINT1("[USBUHCI] Failed to start controller Status %x\n", Status);
|
|
ASSERT(FALSE);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Set the configure bit
|
|
//
|
|
WriteRegister16(UHCI_USBCMD, ReadRegister16(UHCI_USBCMD) | UHCI_USBCMD_CF);
|
|
|
|
for(Index = 0; Index < 2; Index++)
|
|
{
|
|
//
|
|
// get port status
|
|
//
|
|
Status = ReadRegister16(UHCI_PORTSC1 + Index * 2);
|
|
|
|
//
|
|
// clear connection change and port suspend
|
|
//
|
|
WriteRegister16(UHCI_PORTSC1 + Index * 2, Status & ~(UHCI_PORTSC_STATCHA | UHCI_PORTSC_SUSPEND));
|
|
}
|
|
|
|
DPRINT1("[USBUHCI] Controller Started\n");
|
|
DPRINT1("[USBUHCI] Controller Status %x\n", ReadRegister16(UHCI_USBSTS));
|
|
DPRINT1("[USBUHCI] Controller Cmd Status %x\n", ReadRegister16(UHCI_USBCMD));
|
|
DPRINT1("[USBUHCI] Controller Interrupt Status %x\n", ReadRegister16(UHCI_USBINTR));
|
|
DPRINT1("[USBUHCI] Controller Frame %x\n", ReadRegister16(UHCI_FRNUM));
|
|
DPRINT1("[USBUHCI] Controller Port Status 0 %x\n", ReadRegister16(UHCI_PORTSC1));
|
|
DPRINT1("[USBUHCI] Controller Port Status 1 %x\n", ReadRegister16(UHCI_PORTSC1 + 2));
|
|
|
|
|
|
// queue timer
|
|
LARGE_INTEGER Expires;
|
|
Expires.QuadPart = -10 * 10000;
|
|
|
|
KeSetTimerEx(&m_SCETimer, Expires, 1000, &m_SCETimerDpc);
|
|
|
|
//
|
|
// done
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
CUSBHardwareDevice::GlobalReset()
|
|
{
|
|
LARGE_INTEGER Timeout;
|
|
|
|
//
|
|
// back up start of modify register
|
|
//
|
|
ASSERT(m_Base);
|
|
UCHAR sofValue = READ_PORT_UCHAR((PUCHAR)((ULONG)m_Base + UHCI_SOFMOD));
|
|
|
|
//
|
|
// perform global reset
|
|
//
|
|
WriteRegister16(UHCI_USBCMD, ReadRegister16(UHCI_USBCMD) | UHCI_USBCMD_GRESET);
|
|
|
|
//
|
|
// delay is 10 ms
|
|
//
|
|
Timeout.QuadPart = 10;
|
|
DPRINT1("Waiting %lu milliseconds for global reset\n", Timeout.LowPart);
|
|
|
|
//
|
|
// convert to 100 ns units (absolute)
|
|
//
|
|
Timeout.QuadPart *= -10000;
|
|
|
|
//
|
|
// perform the wait
|
|
//
|
|
KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
|
|
|
|
//
|
|
// clear command register
|
|
//
|
|
WriteRegister16(UHCI_USBCMD, ReadRegister16(UHCI_USBCMD) & ~UHCI_USBCMD_GRESET);
|
|
KeStallExecutionProcessor(10);
|
|
|
|
|
|
//
|
|
// restore start of modify register
|
|
//
|
|
WRITE_PORT_UCHAR((PUCHAR)((ULONG)m_Base + UHCI_SOFMOD), sofValue);
|
|
}
|
|
|
|
NTSTATUS
|
|
CUSBHardwareDevice::InitializeController()
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG Index;
|
|
BUS_INTERFACE_STANDARD BusInterface;
|
|
USHORT Value;
|
|
PHYSICAL_ADDRESS Address;
|
|
|
|
DPRINT1("[USBUHCI] InitializeController\n");
|
|
|
|
//
|
|
// now disable all interrupts
|
|
//
|
|
WriteRegister16(UHCI_USBINTR, 0);
|
|
|
|
|
|
//
|
|
// UHCI has two ports
|
|
//
|
|
m_NumberOfPorts = 2;
|
|
|
|
//
|
|
// get bus interface
|
|
//
|
|
Status = GetBusInterface(m_PhysicalDeviceObject, &BusInterface);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to get BusInteface!\n");
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// reclaim ownership from BIOS
|
|
//
|
|
Value = 0;
|
|
BusInterface.GetBusData(BusInterface.Context, PCI_WHICHSPACE_CONFIG, &Value, PCI_LEGSUP, sizeof(USHORT));
|
|
DPRINT1("[USBUHCI] LEGSUP %x\n", Value);
|
|
|
|
Value = PCI_LEGSUP_USBPIRQDEN;
|
|
BusInterface.SetBusData(BusInterface.Context, PCI_WHICHSPACE_CONFIG, &Value, PCI_LEGSUP, sizeof(USHORT));
|
|
|
|
DPRINT1("[USBUHCI] Acquired ownership\n");
|
|
Value = 0;
|
|
BusInterface.GetBusData(BusInterface.Context, PCI_WHICHSPACE_CONFIG, &Value, 0x60, sizeof(UCHAR));
|
|
DPRINT1("[USBUHCI] SBRN %x\n", Value);
|
|
|
|
//
|
|
// perform global reset
|
|
//
|
|
GlobalReset();
|
|
|
|
//
|
|
// reset controller
|
|
//
|
|
Status = ResetController();
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// failed to reset controller
|
|
//
|
|
DPRINT1("[USBUHCI] Failed to reset controller\n");
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// allocate frame list
|
|
//
|
|
Status = m_MemoryManager->Allocate(NUMBER_OF_FRAMES * sizeof(ULONG), (PVOID*)&m_FrameList, &m_FrameListPhysicalAddress);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// failed to allocate frame list
|
|
//
|
|
DPRINT1("[USBUHCI] Failed to allocate frame list with %x\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Set base pointer and reset frame number
|
|
//
|
|
WriteRegister32(UHCI_FRBASEADD, m_FrameListPhysicalAddress.LowPart);
|
|
WriteRegister16(UHCI_FRNUM, 0);
|
|
|
|
//
|
|
// Set the max packet size for bandwidth reclamation to 64 bytes
|
|
//
|
|
WriteRegister16(UHCI_USBCMD, ReadRegister16(UHCI_USBCMD) | UHCI_USBCMD_MAXP);
|
|
|
|
//
|
|
// now create queues
|
|
// 0: interrupt transfers
|
|
// 1: low speed control transfers
|
|
// 2: full speed control transfers
|
|
// 3: bulk transfers
|
|
// 4: debug queue
|
|
//
|
|
for(Index = 0; Index < 5; Index++)
|
|
{
|
|
//
|
|
// create queue head
|
|
//
|
|
Status = m_MemoryManager->Allocate(sizeof(UHCI_QUEUE_HEAD), (PVOID*)&m_QueueHead[Index], &Address);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// failed to allocate queue head
|
|
//
|
|
DPRINT1("[USBUHCI] Failed to allocate queue head %x Index %x\n", Status, Index);
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// store queue head
|
|
//
|
|
m_QueueHead[Index]->PhysicalAddress = Address.LowPart;
|
|
m_QueueHead[Index]->ElementPhysical = QH_TERMINATE;
|
|
m_QueueHead[Index]->LinkPhysical = QH_TERMINATE;
|
|
|
|
if (Index > 0)
|
|
{
|
|
//
|
|
// link queue heads
|
|
//
|
|
m_QueueHead[Index-1]->LinkPhysical = m_QueueHead[Index]->PhysicalAddress | QH_NEXT_IS_QH;
|
|
m_QueueHead[Index-1]->NextLogicalDescriptor = m_QueueHead[Index];
|
|
}
|
|
}
|
|
|
|
DPRINT1("Index %d QueueHead %p LinkPhysical %x ElementPhysical %x PhysicalAddress %x Request %p NextElementDescriptor %p\n",
|
|
0,
|
|
m_QueueHead[0],
|
|
m_QueueHead[0]->LinkPhysical,
|
|
m_QueueHead[0]->ElementPhysical,
|
|
m_QueueHead[0]->PhysicalAddress,
|
|
m_QueueHead[0]->Request,
|
|
m_QueueHead[0]->NextElementDescriptor);
|
|
DPRINT1("Index %d QueueHead %p LinkPhysical %x ElementPhysical %x PhysicalAddress %x Request %p NextElementDescriptor %p\n",
|
|
1,
|
|
m_QueueHead[1],
|
|
m_QueueHead[1]->LinkPhysical,
|
|
m_QueueHead[1]->ElementPhysical,
|
|
m_QueueHead[1]->PhysicalAddress,
|
|
m_QueueHead[1]->Request,
|
|
m_QueueHead[1]->NextElementDescriptor);
|
|
|
|
DPRINT1("Index %d QueueHead %p LinkPhysical %x ElementPhysical %x PhysicalAddress %x Request %p NextElementDescriptor %p\n",
|
|
2,
|
|
m_QueueHead[2],
|
|
m_QueueHead[2]->LinkPhysical,
|
|
m_QueueHead[2]->ElementPhysical,
|
|
m_QueueHead[2]->PhysicalAddress,
|
|
m_QueueHead[2]->Request,
|
|
m_QueueHead[2]->NextElementDescriptor);
|
|
DPRINT1("Index %d QueueHead %p LinkPhysical %x ElementPhysical %x PhysicalAddress %x Request %p NextElementDescriptor %p\n",
|
|
3,
|
|
m_QueueHead[3],
|
|
m_QueueHead[3]->LinkPhysical,
|
|
m_QueueHead[3]->ElementPhysical,
|
|
m_QueueHead[3]->PhysicalAddress,
|
|
m_QueueHead[3]->Request,
|
|
m_QueueHead[3]->NextElementDescriptor);
|
|
DPRINT1("Index %d QueueHead %p LinkPhysical %x ElementPhysical %x PhysicalAddress %x Request %p NextElementDescriptor %p\n",
|
|
4,
|
|
m_QueueHead[4],
|
|
m_QueueHead[4]->LinkPhysical,
|
|
m_QueueHead[4]->ElementPhysical,
|
|
m_QueueHead[4]->PhysicalAddress,
|
|
m_QueueHead[4]->Request,
|
|
m_QueueHead[4]->NextElementDescriptor);
|
|
|
|
//
|
|
// terminate last queue head with stray descriptor
|
|
//
|
|
Status = m_MemoryManager->Allocate(sizeof(UHCI_TRANSFER_DESCRIPTOR), (PVOID*)&m_StrayDescriptor, &m_StrayDescriptorPhysicalAddress);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// failed to allocate queue head
|
|
//
|
|
DPRINT1("[USBUHCI] Failed to allocate queue head %x Index %x\n", Status, Index);
|
|
return Status;
|
|
}
|
|
#if 0
|
|
//
|
|
// init stray descriptor
|
|
//
|
|
m_StrayDescriptor->PhysicalAddress = m_StrayDescriptorPhysicalAddress.LowPart;
|
|
m_StrayDescriptor->LinkPhysical = TD_TERMINATE;
|
|
m_StrayDescriptor->Token = TD_TOKEN_NULL_DATA | (0x7f << TD_TOKEN_DEVADDR_SHIFT) | TD_TOKEN_IN;
|
|
|
|
|
|
//
|
|
// link to last queue head
|
|
//
|
|
m_QueueHead[4]->LinkPhysical = m_StrayDescriptor->PhysicalAddress;
|
|
m_QueueHead[4]->NextLogicalDescriptor = m_StrayDescriptor;
|
|
#endif
|
|
|
|
//
|
|
// allocate frame bandwidth array
|
|
//
|
|
m_FrameBandwidth = (PUSHORT)ExAllocatePool(NonPagedPool, sizeof(USHORT) * NUMBER_OF_FRAMES);
|
|
if (!m_FrameBandwidth)
|
|
{
|
|
//
|
|
// no memory
|
|
//
|
|
DPRINT1("[USBUHCI] Failed to allocate memory\n");
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// init frame list
|
|
//
|
|
for (Index = 0; Index < NUMBER_OF_FRAMES; Index++)
|
|
{
|
|
//
|
|
// store frame list interrupt queue
|
|
//
|
|
m_FrameList[Index] = m_QueueHead[UHCI_INTERRUPT_QUEUE]->PhysicalAddress | FRAMELIST_NEXT_IS_QH;
|
|
m_FrameBandwidth[Index] = MAX_AVAILABLE_BANDWIDTH;
|
|
|
|
|
|
}
|
|
|
|
//
|
|
// set enabled interrupt mask
|
|
//
|
|
m_InterruptMask = UHCI_USBSTS_USBINT | UHCI_USBSTS_ERRINT | UHCI_USBSTS_HOSTERR | UHCI_USBSTS_HCPRERR | UHCI_USBSTS_HCHALT;
|
|
|
|
//
|
|
// now enable interrupts
|
|
//
|
|
WriteRegister16(UHCI_USBINTR, UHCI_USBINTR_CRC | UHCI_USBINTR_IOC| UHCI_USBINTR_SHORT);
|
|
|
|
DPRINT1("[USBUHCI] Controller initialized\n");
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
CUSBHardwareDevice::StopController(void)
|
|
{
|
|
ASSERT(FALSE);
|
|
//
|
|
// failed to reset controller
|
|
//
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
NTSTATUS
|
|
CUSBHardwareDevice::ResetController(void)
|
|
{
|
|
ULONG Count = 0;
|
|
USHORT Status;
|
|
|
|
// clear run bit
|
|
WriteRegister16(UHCI_USBCMD, ReadRegister16(UHCI_USBCMD) & ~UHCI_USBCMD_RS);
|
|
|
|
// wait for the controller to stop
|
|
while((ReadRegister16(UHCI_USBSTS) & UHCI_USBSTS_HCHALT) == 0)
|
|
{
|
|
DPRINT1("[UHCI] Waiting for the controller to halt\n");
|
|
KeStallExecutionProcessor(10);
|
|
}
|
|
|
|
// clear configure bit
|
|
WriteRegister16(UHCI_USBCMD, ReadRegister16(UHCI_USBCMD) & ~UHCI_USBCMD_CF);
|
|
|
|
//
|
|
// reset controller
|
|
//
|
|
WriteRegister16(UHCI_USBCMD, UHCI_USBCMD_HCRESET);
|
|
|
|
do
|
|
{
|
|
//
|
|
// wait a bit
|
|
//
|
|
KeStallExecutionProcessor(100);
|
|
|
|
//
|
|
// get status
|
|
//
|
|
Status = ReadRegister16(UHCI_USBCMD);
|
|
if (!(Status & UHCI_USBCMD_HCRESET))
|
|
{
|
|
//
|
|
// controller reset completed
|
|
//
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}while(Count++ < 100);
|
|
|
|
DPRINT1("[USBUHCI] Failed to reset controller Status %x\n", Status);
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
NTSTATUS
|
|
CUSBHardwareDevice::ResetPort(
|
|
IN ULONG PortIndex)
|
|
{
|
|
ULONG Port;
|
|
USHORT Status;
|
|
ULONG Index;
|
|
LARGE_INTEGER Timeout;
|
|
|
|
DPRINT1("[UHCI] ResetPort Id %lu\n", PortIndex);
|
|
|
|
//
|
|
// sanity check
|
|
//
|
|
ASSERT(PortIndex <= 1);
|
|
|
|
//
|
|
// get register offset
|
|
//
|
|
Port = UHCI_PORTSC1 + PortIndex * 2;
|
|
|
|
//
|
|
// read port status
|
|
//
|
|
Status = ReadRegister16(Port);
|
|
|
|
|
|
|
|
//
|
|
// remove unwanted bits
|
|
//
|
|
Status &= UHCI_PORTSC_DATAMASK;
|
|
|
|
//
|
|
// now reset the port
|
|
//
|
|
WriteRegister16(Port, Status | UHCI_PORTSC_RESET);
|
|
|
|
//
|
|
// delay is 20 ms for port reset
|
|
//
|
|
Timeout.QuadPart = 20;
|
|
DPRINT1("Waiting %lu milliseconds for port reset\n", Timeout.LowPart);
|
|
|
|
//
|
|
// convert to 100 ns units (absolute)
|
|
//
|
|
Timeout.QuadPart *= -10000;
|
|
|
|
//
|
|
// perform the wait
|
|
//
|
|
KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
|
|
|
|
//
|
|
// re-read status
|
|
//
|
|
Status = ReadRegister16(Port);
|
|
|
|
//
|
|
// remove unwanted bits
|
|
//
|
|
Status &= UHCI_PORTSC_DATAMASK;
|
|
|
|
//
|
|
// clear reset port
|
|
//
|
|
WriteRegister16(Port, (Status & ~UHCI_PORTSC_RESET));
|
|
|
|
|
|
//
|
|
// now wait a bit
|
|
//
|
|
KeStallExecutionProcessor(10);
|
|
|
|
for (Index = 0; Index < 100; Index++)
|
|
{
|
|
// read port status
|
|
Status = ReadRegister16(Port);
|
|
|
|
// remove unwanted bits
|
|
Status &= UHCI_PORTSC_DATAMASK;
|
|
|
|
// enable port
|
|
WriteRegister16(Port, Status | UHCI_PORTSC_ENABLED);
|
|
|
|
//
|
|
// wait a bit
|
|
//
|
|
KeStallExecutionProcessor(50);
|
|
|
|
//
|
|
// re-read port
|
|
//
|
|
Status = ReadRegister16(Port);
|
|
|
|
if ((Status & UHCI_PORTSC_CURSTAT) == 0)
|
|
{
|
|
// no device connected. since we waited long enough we can assume
|
|
// that the port was reset and no device is connected.
|
|
break;
|
|
}
|
|
|
|
if (Status & (UHCI_PORTSC_STATCHA | UHCI_PORTSC_ENABCHA))
|
|
{
|
|
// port enabled changed or connection status were set.
|
|
// acknowledge either / both and wait again.
|
|
WriteRegister16(Port, Status);
|
|
continue;
|
|
}
|
|
|
|
if (Status & UHCI_PORTSC_ENABLED)
|
|
{
|
|
// the port is enabled
|
|
break;
|
|
}
|
|
}
|
|
|
|
m_PortResetChange |= (1 << PortIndex);
|
|
DPRINT1("[USBUhci] Port Index %x Status after reset %x\n", PortIndex, ReadRegister16(Port));
|
|
|
|
//
|
|
// is there a callback
|
|
//
|
|
if (m_SCECallBack)
|
|
{
|
|
//
|
|
// issue callback
|
|
//
|
|
m_SCECallBack(m_SCEContext);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
CUSBHardwareDevice::GetPortStatus(
|
|
ULONG PortId,
|
|
OUT USHORT *PortStatus,
|
|
OUT USHORT *PortChange)
|
|
{
|
|
USHORT Status;
|
|
|
|
//
|
|
// sanity check
|
|
//
|
|
if (PortId > 1)
|
|
{
|
|
//
|
|
// invalid index
|
|
//
|
|
DPRINT1("[UHCI] Invalid PortIndex %lu\n", PortId);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// init status
|
|
//
|
|
*PortStatus = 0;
|
|
*PortChange = 0;
|
|
|
|
//
|
|
// read port status
|
|
//
|
|
Status = ReadRegister16(UHCI_PORTSC1 + PortId * 2);
|
|
DPRINT("[USBUHCI] PortId %x Status %x\n", PortId, Status);
|
|
|
|
// build the status
|
|
if (Status & UHCI_PORTSC_CURSTAT)
|
|
{
|
|
*PortStatus |= USB_PORT_STATUS_CONNECT;
|
|
}
|
|
|
|
if (Status & UHCI_PORTSC_ENABLED)
|
|
{
|
|
*PortStatus |= USB_PORT_STATUS_ENABLE;
|
|
}
|
|
|
|
if (Status & UHCI_PORTSC_RESET)
|
|
{
|
|
*PortStatus |= USB_PORT_STATUS_RESET;
|
|
}
|
|
|
|
if (Status & UHCI_PORTSC_LOWSPEED)
|
|
{
|
|
*PortStatus |= USB_PORT_STATUS_LOW_SPEED;
|
|
}
|
|
|
|
if (Status & UHCI_PORTSC_STATCHA)
|
|
{
|
|
*PortChange |= USB_PORT_STATUS_CONNECT;
|
|
}
|
|
|
|
if (Status & UHCI_PORTSC_ENABCHA)
|
|
{
|
|
*PortChange |= USB_PORT_STATUS_ENABLE;
|
|
}
|
|
|
|
if (m_PortResetChange & (1 << PortId))
|
|
{
|
|
*PortChange |= USB_PORT_STATUS_RESET;
|
|
}
|
|
|
|
//
|
|
// port always has power
|
|
//
|
|
*PortStatus |= USB_PORT_STATUS_POWER;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
CUSBHardwareDevice::ClearPortStatus(
|
|
ULONG PortId,
|
|
ULONG Feature)
|
|
{
|
|
ULONG PortRegister;
|
|
USHORT PortStatus;
|
|
|
|
DPRINT("CUSBHardwareDevice::ClearPortStatus PortId %x Feature %x\n", PortId, Feature);
|
|
|
|
//
|
|
// sanity check
|
|
//
|
|
if (PortId > 1)
|
|
{
|
|
//
|
|
// invalid index
|
|
//
|
|
DPRINT1("[UHCI] Invalid PortIndex %lu\n", PortId);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// read current status
|
|
//
|
|
PortRegister = UHCI_PORTSC1 + PortId * 2;
|
|
PortStatus = ReadRegister16(PortRegister);
|
|
DPRINT("[UHCI] PortStatus %x\n", PortStatus);
|
|
|
|
if (Feature == C_PORT_RESET)
|
|
{
|
|
//
|
|
// UHCI is not supporting port reset register bit
|
|
//
|
|
m_PortResetChange &= ~(1 << PortId);
|
|
}
|
|
else if (Feature == C_PORT_CONNECTION || Feature == C_PORT_ENABLE)
|
|
{
|
|
//
|
|
// clear port status changes
|
|
//
|
|
WriteRegister16(PortRegister, PortStatus);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CUSBHardwareDevice::SetPortFeature(
|
|
ULONG PortId,
|
|
ULONG Feature)
|
|
{
|
|
ULONG PortRegister;
|
|
|
|
DPRINT1("[UHCI] SetPortFeature PortId %x Feature %x\n", PortId, Feature);
|
|
|
|
//
|
|
// sanity check
|
|
//
|
|
if (PortId > 1)
|
|
{
|
|
//
|
|
// invalid index
|
|
//
|
|
DPRINT1("[UHCI] Invalid PortIndex %lu\n", PortId);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
PortRegister = UHCI_PORTSC1 + PortId * 2;
|
|
|
|
if (Feature == PORT_RESET)
|
|
{
|
|
//
|
|
// reset port
|
|
//
|
|
return ResetPort(PortId);
|
|
}
|
|
else if (Feature == PORT_ENABLE)
|
|
{
|
|
//
|
|
// reset port
|
|
//
|
|
WriteRegister16(PortRegister, ReadRegister16(PortRegister) | UHCI_PORTSC_ENABLED);
|
|
}
|
|
else if (Feature == PORT_POWER)
|
|
{
|
|
//
|
|
// port power is no op, it is always enabled
|
|
//
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
CUSBHardwareDevice::SetStatusChangeEndpointCallBack(
|
|
PVOID CallBack,
|
|
PVOID Context)
|
|
{
|
|
m_SCECallBack = (HD_INIT_CALLBACK*)CallBack;
|
|
m_SCEContext = Context;
|
|
}
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
InterruptServiceRoutine(
|
|
IN PKINTERRUPT Interrupt,
|
|
IN PVOID ServiceContext)
|
|
{
|
|
CUSBHardwareDevice *This;
|
|
USHORT Status, Acknowledge;
|
|
|
|
//
|
|
// get context
|
|
//
|
|
This = (CUSBHardwareDevice*) ServiceContext;
|
|
|
|
//
|
|
// read register
|
|
//
|
|
Status = This->ReadRegister16(UHCI_USBSTS);
|
|
DPRINT("InterruptServiceRoutine %x\n", Status);
|
|
|
|
//
|
|
// check if the interrupt signaled are from us
|
|
//
|
|
if ((Status & This->m_InterruptMask) == 0)
|
|
{
|
|
if (Status != 0)
|
|
{
|
|
//
|
|
// FIXME: received unexpected interrupt
|
|
//
|
|
DPRINT1("[USBUHCI] Unexpected interrupt %x\n", Status);
|
|
This->WriteRegister16(UHCI_USBSTS, Status);
|
|
}
|
|
|
|
//
|
|
// shared interrupt
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// check for the interrupt cause
|
|
//
|
|
Acknowledge = 0;
|
|
|
|
if (Status & UHCI_USBSTS_USBINT)
|
|
{
|
|
//
|
|
// transfer finished
|
|
//
|
|
Acknowledge |= UHCI_USBSTS_USBINT;
|
|
}
|
|
|
|
if (Status & UHCI_USBSTS_ERRINT)
|
|
{
|
|
//
|
|
// error interrupt
|
|
//
|
|
Acknowledge |= UHCI_USBSTS_ERRINT;
|
|
DPRINT1("[UHCI] Error interrupt\n");
|
|
}
|
|
|
|
if (Status & UHCI_USBSTS_RESDET)
|
|
{
|
|
//
|
|
// resume detected
|
|
//
|
|
DPRINT1("[UHCI] Resume detected\n");
|
|
Acknowledge |= UHCI_USBSTS_RESDET;
|
|
}
|
|
|
|
if (Status & UHCI_USBSTS_HOSTERR)
|
|
{
|
|
//
|
|
// host system error
|
|
//
|
|
DPRINT1("[UHCI] Host System Error\n");
|
|
Acknowledge |= UHCI_USBSTS_HOSTERR;
|
|
}
|
|
|
|
if (Status & UHCI_USBSTS_HCPRERR)
|
|
{
|
|
//
|
|
// processing error
|
|
//
|
|
DPRINT1("[UHCI] Process Error\n");
|
|
Acknowledge |= UHCI_USBSTS_HCPRERR;
|
|
}
|
|
|
|
if (Status & UHCI_USBSTS_HCHALT)
|
|
{
|
|
//
|
|
// controller halted
|
|
//
|
|
DPRINT1("[UHCI] Host controller halted\n");
|
|
|
|
//
|
|
// disable interrupts
|
|
//
|
|
This->WriteRegister16(UHCI_USBINTR, 0);
|
|
This->m_InterruptMask = 0;
|
|
}
|
|
|
|
//
|
|
// do we have something to acknowledge
|
|
//
|
|
if (Acknowledge)
|
|
{
|
|
//
|
|
// acknowledge interrupt
|
|
//
|
|
This->WriteRegister16(UHCI_USBSTS, Acknowledge);
|
|
|
|
//
|
|
// queue dpc
|
|
//
|
|
KeInsertQueueDpc(&This->m_IntDpcObject, UlongToPtr(Status), NULL);
|
|
}
|
|
|
|
//
|
|
// interrupt handled
|
|
//
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
CUSBHardwareDevice::WriteRegister8(
|
|
IN ULONG Register,
|
|
IN UCHAR Value)
|
|
{
|
|
WRITE_PORT_UCHAR((PUCHAR)((ULONG)m_Base + Register), Value);
|
|
}
|
|
|
|
|
|
VOID
|
|
CUSBHardwareDevice::WriteRegister16(
|
|
ULONG Register,
|
|
USHORT Value)
|
|
{
|
|
WRITE_PORT_USHORT((PUSHORT)((ULONG)m_Base + Register), Value);
|
|
}
|
|
|
|
|
|
VOID
|
|
CUSBHardwareDevice::WriteRegister32(
|
|
ULONG Register,
|
|
ULONG Value)
|
|
{
|
|
WRITE_PORT_ULONG((PULONG)((ULONG)m_Base + Register), Value);
|
|
}
|
|
|
|
|
|
UCHAR
|
|
CUSBHardwareDevice::ReadRegister8(
|
|
ULONG Register)
|
|
{
|
|
return READ_PORT_UCHAR((PUCHAR)((ULONG)m_Base + Register));
|
|
}
|
|
|
|
|
|
USHORT
|
|
CUSBHardwareDevice::ReadRegister16(
|
|
ULONG Register)
|
|
{
|
|
return READ_PORT_USHORT((PUSHORT)((ULONG)m_Base + Register));
|
|
}
|
|
|
|
|
|
ULONG
|
|
CUSBHardwareDevice::ReadRegister32(
|
|
ULONG Register)
|
|
{
|
|
return READ_PORT_ULONG((PULONG)((ULONG)m_Base + Register));
|
|
}
|
|
|
|
VOID
|
|
CUSBHardwareDevice::GetQueueHead(
|
|
IN ULONG QueueHeadIndex,
|
|
OUT PUHCI_QUEUE_HEAD *OutQueueHead)
|
|
{
|
|
//
|
|
// sanity check
|
|
//
|
|
ASSERT(QueueHeadIndex < 5);
|
|
|
|
//
|
|
// store queue head
|
|
//
|
|
*OutQueueHead = m_QueueHead[QueueHeadIndex];
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
UhciDefferedRoutine(
|
|
IN PKDPC Dpc,
|
|
IN PVOID DeferredContext,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2)
|
|
{
|
|
CUSBHardwareDevice *This;
|
|
ULONG Status;
|
|
|
|
//
|
|
// get parameters
|
|
//
|
|
This = (CUSBHardwareDevice*)DeferredContext;
|
|
|
|
DPRINT("UhciDefferedRoutine\n");
|
|
|
|
//
|
|
// get status
|
|
//
|
|
Status = PtrToUlong(SystemArgument1);
|
|
if (Status & (UHCI_USBSTS_USBINT | UHCI_USBSTS_ERRINT))
|
|
{
|
|
//
|
|
// a transfer finished, inform the queue
|
|
//
|
|
This->m_UsbQueue->TransferInterrupt(Status & UHCI_USBSTS_USBINT);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// other event
|
|
//
|
|
DPRINT1("[USBUHCI] Status %x not handled\n", Status);
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
TimerDpcRoutine(
|
|
IN PKDPC Dpc,
|
|
IN PVOID DeferredContext,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2)
|
|
{
|
|
CUSBHardwareDevice *This;
|
|
USHORT PortStatus = 0;
|
|
USHORT PortChange = 0;
|
|
|
|
// get parameters
|
|
This = (CUSBHardwareDevice*)DeferredContext;
|
|
|
|
// check port 0
|
|
This->GetPortStatus(0, &PortStatus, &PortChange);
|
|
if (PortChange)
|
|
{
|
|
// invoke status change work item routine
|
|
StatusChangeWorkItemRoutine(DeferredContext);
|
|
return;
|
|
}
|
|
|
|
// check port 1
|
|
This->GetPortStatus(1, &PortStatus, &PortChange);
|
|
if (PortChange)
|
|
{
|
|
// invoke status change work item routine
|
|
StatusChangeWorkItemRoutine(DeferredContext);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
NTAPI
|
|
StatusChangeWorkItemRoutine(
|
|
PVOID Context)
|
|
{
|
|
//
|
|
// cast to hardware object
|
|
//
|
|
CUSBHardwareDevice * This = (CUSBHardwareDevice*)Context;
|
|
|
|
//
|
|
// is there a callback
|
|
//
|
|
if (This->m_SCECallBack)
|
|
{
|
|
//
|
|
// issue callback
|
|
//
|
|
This->m_SCECallBack(This->m_SCEContext);
|
|
}
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
CreateUSBHardware(
|
|
PUSBHARDWAREDEVICE *OutHardware)
|
|
{
|
|
PUSBHARDWAREDEVICE This;
|
|
|
|
This = new(NonPagedPool, TAG_USBUHCI) CUSBHardwareDevice(0);
|
|
|
|
if (!This)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
This->AddRef();
|
|
|
|
// return result
|
|
*OutHardware = (PUSBHARDWAREDEVICE)This;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|