reactos/drivers/usb/usbehci/fdo.c
Johannes Anderwald 2e8b524c99 [USBEHCI]
- Re-implement DMA buffer routines. It is now faster and consumes less memory overhead per allocated memory block.
- IoGetDeviceProperty needs a PDO
- Add few comments
- No need to clear buffer twice in CreateQueueHead / CreateDescriptor




svn path=/branches/cmake-bringup/; revision=51324
2011-04-13 01:37:14 +00:00

1079 lines
38 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/fdo.c
* PURPOSE: USB EHCI device driver.
* PROGRAMMERS:
* Michael Martin (michael.martin@reactos.org)
*/
#include "hwiface.h"
#include "usbehci.h"
#include "physmem.h"
#include <stdio.h>
VOID NTAPI
EhciDefferedRoutine(PKDPC Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2)
{
PFDO_DEVICE_EXTENSION FdoDeviceExtension;
PPDO_DEVICE_EXTENSION PdoDeviceExtension;
ULONG CStatus;
ULONG tmp;
ULONG OpRegisters;
PEHCI_HOST_CONTROLLER hcd;
LONG i;
ULONG CurrentAddr, OffSet;
PQUEUE_HEAD CompletedQH, NextQH;
PQUEUE_TRANSFER_DESCRIPTOR CompletedTD, NextTD;
FdoDeviceExtension = (PFDO_DEVICE_EXTENSION) DeferredContext;
if (!FdoDeviceExtension->Pdo)
{
DPRINT1("PDO not set yet!\n");
return;
}
PdoDeviceExtension = (PPDO_DEVICE_EXTENSION) FdoDeviceExtension->Pdo->DeviceExtension;
OpRegisters = (ULONG)FdoDeviceExtension->hcd.OpRegisters;
hcd = &FdoDeviceExtension->hcd;
CStatus = (ULONG) SystemArgument2;
/* TD retired or Error */
if (CStatus & (EHCI_STS_INT | EHCI_ERROR_INT))
{
DPRINT("Asyn Complete!\n");
/* AsyncListAddr Register will have the next QueueHead to execute */
CurrentAddr = GetAsyncListQueueRegister(hcd);
/* Calculate the VA for the next QueueHead */
OffSet = CurrentAddr - (ULONG)FdoDeviceExtension->hcd.CommonBufferPA.LowPart;
NextQH = (PQUEUE_HEAD)((ULONG)FdoDeviceExtension->hcd.CommonBufferVA + OffSet);
/* Get the previous QueueHead which is the QueueHead just completed */
CompletedQH = NextQH->PreviousQueueHead;
ASSERT(CompletedQH);
DPRINT("CompletedQH %x\n", CompletedQH);
//DumpQueueHead(CompletedQH);
/* Free memory for the Descriptors */
CompletedTD = CompletedQH->TransferDescriptor;
NextTD = CompletedTD;
while (NextTD)
{
CompletedTD = NextTD;
NextTD = NextTD->NextDescriptor;
FreeDescriptor(hcd, CompletedTD);
}
/* If the Event is set then release waiter */
if (CompletedQH->Event)
{
KeSetEvent(CompletedQH->Event, IO_NO_INCREMENT, FALSE);
}
/* Free the Mdl if there was one */
if(CompletedQH->MdlToFree)
IoFreeMdl(CompletedQH->MdlToFree);
/* Is there an IRP that needs to be completed */
if (CompletedQH->IrpToComplete)
{
PIRP Irp;
PIO_STACK_LOCATION Stack;
PURB Urb;
Irp = CompletedQH->IrpToComplete;
Stack = IoGetCurrentIrpStackLocation(Irp);
ASSERT(Stack);
Urb = (PURB) Stack->Parameters.Others.Argument1;
/* Check for error */
if (CStatus & EHCI_ERROR_INT)
{
/* Haled bit should be set */
if (CompletedQH->Token.Bits.Halted)
{
if (CompletedQH->Token.Bits.DataBufferError)
{
DPRINT1("Data buffer error\n");
Urb->UrbHeader.Status = USBD_STATUS_DATA_BUFFER_ERROR;
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
Irp->IoStatus.Information = 0;
}
else if (CompletedQH->Token.Bits.BabbleDetected)
{
DPRINT1("Babble Detected\n");
Urb->UrbHeader.Status = USBD_STATUS_BABBLE_DETECTED;
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
Irp->IoStatus.Information = 0;
}
else
{
DPRINT1("Stall PID\n");
Urb->UrbHeader.Status = USBD_STATUS_STALL_PID;
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
Irp->IoStatus.Information = 0;
}
}
}
else
{
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
DPRINT1("Completing Irp\n");
}
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
/* Unlink QueueHead */
UnlinkQueueHead(hcd, CompletedQH);
/* Wait for a complete AsnycList tranversal before deleting? */
DeleteQueueHead(hcd, CompletedQH);
}
/* Port Change */
if (CStatus & EHCI_STS_PCD)
{
/* Loop through the ports */
for (i = 0; i < FdoDeviceExtension->hcd.ECHICaps.HCSParams.PortCount; i++)
{
tmp = READ_REGISTER_ULONG((PULONG) ((OpRegisters + EHCI_PORTSC) + (4 * i)));
/* Check for port change on this port */
if (tmp & 0x02)
{
/* Connect or Disconnect? */
if (tmp & 0x01)
{
DPRINT1("Device connected on port %d\n", i);
/* Check if a companion host controller exists */
if (FdoDeviceExtension->hcd.ECHICaps.HCSParams.CHCCount)
{
tmp = READ_REGISTER_ULONG((PULONG)((OpRegisters + EHCI_PORTSC) + (4 * i)));
/* Port should be in disabled state, as per USB 2.0 specs */
if (tmp & 0x04)
{
DPRINT1("Warning: The port the device has just connected to is not disabled!\n");
}
/* Is this non high speed device */
if (tmp & 0x400)
{
DPRINT1("Non HighSpeed device connected. Releasing ownership.\n");
/* Release ownership to companion host controller */
WRITE_REGISTER_ULONG((PULONG) ((OpRegisters + EHCI_PORTSC) + (4 * i)), 0x2000);
continue;
}
}
KeStallExecutionProcessor(30);
/* As per USB 2.0 Specs, 9.1.2. Reset the port and clear the status change */
//tmp |= 0x100 | 0x02;
/* Sanity, Disable port */
//tmp &= ~0x04;
//WRITE_REGISTER_ULONG((PULONG) ((Base + EHCI_PORTSC) + (4 * i)), tmp);
//KeStallExecutionProcessor(20);
tmp = READ_REGISTER_ULONG((PULONG)((OpRegisters + EHCI_PORTSC) + (4 * i)));
PdoDeviceExtension->ChildDeviceCount++;
PdoDeviceExtension->Ports[i].PortStatus &= ~0x8000;
PdoDeviceExtension->Ports[i].PortStatus |= USB_PORT_STATUS_HIGH_SPEED;
PdoDeviceExtension->Ports[i].PortStatus |= USB_PORT_STATUS_CONNECT;
PdoDeviceExtension->Ports[i].PortChange |= USB_PORT_STATUS_CONNECT;
DPRINT1("Completing URB\n");
CompletePendingURBRequest(PdoDeviceExtension);
}
else
{
DPRINT1("Device disconnected on port %d\n", i);
/* Clear status change */
tmp = READ_REGISTER_ULONG((PULONG)((OpRegisters + EHCI_PORTSC) + (4 * i)));
tmp |= 0x02;
WRITE_REGISTER_ULONG((PULONG) ((OpRegisters + EHCI_PORTSC) + (4 * i)), tmp);
}
}
}
}
/* Asnyc Advance */
if (CStatus & EHCI_STS_IAA)
{
DPRINT1("Async Advance!\n");
}
}
BOOLEAN NTAPI
InterruptService(PKINTERRUPT Interrupt, PVOID ServiceContext)
{
PFDO_DEVICE_EXTENSION FdoDeviceExtension;
PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT) ServiceContext;
PEHCI_HOST_CONTROLLER hcd;
ULONG CStatus = 0;
FdoDeviceExtension = (PFDO_DEVICE_EXTENSION) DeviceObject->DeviceExtension;
hcd = &FdoDeviceExtension->hcd;
/* Read device status */
CStatus = ReadControllerStatus(hcd);
CStatus &= (EHCI_ERROR_INT | EHCI_STS_INT | EHCI_STS_IAA | EHCI_STS_PCD | EHCI_STS_FLR);
if ((!CStatus) || (FdoDeviceExtension->DeviceState == 0))
{
/* This interrupt isnt for us or not ready for it. */
return FALSE;
}
/* Clear status */
ClearControllerStatus(hcd, CStatus);
if (CStatus & EHCI_ERROR_INT)
{
DPRINT1("EHCI Status=0x%x\n", CStatus);
}
if (CStatus & EHCI_STS_FATAL)
{
DPRINT1("EHCI: Host System Error. Possible PCI problems.\n");
ASSERT(FALSE);
}
if (CStatus & EHCI_STS_HALT)
{
DPRINT1("EHCI: Host Controller unexpected halt.\n");
/* FIXME: Reset the controller */
}
KeInsertQueueDpc(&FdoDeviceExtension->DpcObject, FdoDeviceExtension, (PVOID)CStatus);
return TRUE;
}
NTSTATUS
StartDevice(PDEVICE_OBJECT DeviceObject, PCM_PARTIAL_RESOURCE_LIST raw, PCM_PARTIAL_RESOURCE_LIST translated)
{
PFDO_DEVICE_EXTENSION FdoDeviceExtension;
PCM_PARTIAL_RESOURCE_DESCRIPTOR resource;
DEVICE_DESCRIPTION DeviceDescription;
ULONG NumberResources;
ULONG iCount;
ULONG DeviceAddress;
ULONG PropertySize;
ULONG BusNumber;
NTSTATUS Status;
FdoDeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
Status = IoGetDeviceProperty(FdoDeviceExtension->Pdo,
DevicePropertyAddress,
sizeof(ULONG),
&DeviceAddress,
&PropertySize);
if (NT_SUCCESS(Status))
{
DPRINT1("--->DeviceAddress: %x\n", DeviceAddress);
}
Status = IoGetDeviceProperty(FdoDeviceExtension->Pdo,
DevicePropertyBusNumber,
sizeof(ULONG),
&BusNumber,
&PropertySize);
if (NT_SUCCESS(Status))
{
DPRINT1("--->BusNumber: %x\n", BusNumber);
}
/* Get the resources the PNP Manager gave */
NumberResources = translated->Count;
DPRINT("NumberResources %d\n", NumberResources);
for (iCount = 0; iCount < NumberResources; iCount++)
{
DPRINT("Resource Info %d:\n", iCount);
resource = &translated->PartialDescriptors[iCount];
switch(resource->Type)
{
case CmResourceTypePort:
{
DPRINT("Port Start: %x\n", resource->u.Port.Start);
DPRINT("Port Length %d\n", resource->u.Port.Length);
/* FIXME: Handle Ports */
break;
}
case CmResourceTypeInterrupt:
{
DPRINT("Interrupt Vector: %x\n", resource->u.Interrupt.Vector);
FdoDeviceExtension->Vector = resource->u.Interrupt.Vector;
FdoDeviceExtension->Irql = resource->u.Interrupt.Level;
FdoDeviceExtension->Affinity = resource->u.Interrupt.Affinity;
FdoDeviceExtension->Mode = (resource->Flags == CM_RESOURCE_INTERRUPT_LATCHED) ? Latched : LevelSensitive;
FdoDeviceExtension->IrqShared = resource->ShareDisposition == CmResourceShareShared;
break;
}
case CmResourceTypeMemory:
{
PVOID ResourceBase = 0;
DPRINT("Mem Start: %x\n", resource->u.Memory.Start);
DPRINT("Mem Length: %d\n", resource->u.Memory.Length);
ResourceBase = MmMapIoSpace(resource->u.Memory.Start, resource->u.Memory.Length, FALSE);
DPRINT("ResourceBase %x\n", ResourceBase);
if (ResourceBase == NULL)
{
DPRINT1("MmMapIoSpace failed!!!!!!!!!\n");
}
GetCapabilities(&FdoDeviceExtension->hcd.ECHICaps, (ULONG)ResourceBase);
DPRINT1("hcd.ECHICaps.Length %x\n", FdoDeviceExtension->hcd.ECHICaps.Length);
FdoDeviceExtension->hcd.OpRegisters = (ULONG)((ULONG)ResourceBase + FdoDeviceExtension->hcd.ECHICaps.Length);
break;
}
case CmResourceTypeDma:
{
DPRINT("Dma Channel: %x\n", resource->u.Dma.Channel);
DPRINT("Dma Port: %d\n", resource->u.Dma.Port);
break;
}
case CmResourceTypeDevicePrivate:
{
/* Windows does this. */
DPRINT1("CmResourceTypeDevicePrivate not handled\n");
break;
}
default:
{
DPRINT1("PNP Manager gave resource type not handled!! Notify Developers!\n");
break;
}
}
}
/* initialize dpc */
KeInitializeDpc(&FdoDeviceExtension->DpcObject,
EhciDefferedRoutine,
FdoDeviceExtension);
RtlZeroMemory(&DeviceDescription, sizeof(DEVICE_DESCRIPTION));
DeviceDescription.Version = DEVICE_DESCRIPTION_VERSION;
DeviceDescription.Master = TRUE;
DeviceDescription.ScatterGather = TRUE;
DeviceDescription.Dma32BitAddresses = TRUE;
DeviceDescription.DmaWidth = 2;
DeviceDescription.InterfaceType = PCIBus;
DeviceDescription.MaximumLength = EHCI_MAX_SIZE_TRANSFER;
FdoDeviceExtension->pDmaAdapter = IoGetDmaAdapter(FdoDeviceExtension->Pdo,
&DeviceDescription,
&FdoDeviceExtension->MapRegisters);
if (FdoDeviceExtension->pDmaAdapter == NULL)
{
DPRINT1("Ehci: IoGetDmaAdapter failed!\n");
ASSERT(FALSE);
}
/* Allocate Common Buffer for Periodic Frame List */
FdoDeviceExtension->PeriodicFrameList.VirtualAddr =
FdoDeviceExtension->pDmaAdapter->DmaOperations->AllocateCommonBuffer(FdoDeviceExtension->pDmaAdapter,
sizeof(ULONG) * 1024, &FdoDeviceExtension->PeriodicFrameList.PhysicalAddr, FALSE);
if (FdoDeviceExtension->PeriodicFrameList.VirtualAddr == NULL)
{
DPRINT1("Ehci: FdoDeviceExtension->PeriodicFramList is null\n");
return STATUS_UNSUCCESSFUL;
}
/* Zeroize it */
RtlZeroMemory(FdoDeviceExtension->PeriodicFrameList.VirtualAddr, sizeof(ULONG) * 1024);
ExInitializeFastMutex(&FdoDeviceExtension->FrameListMutex);
/* Allocate pages for queueheads and descriptors */
FdoDeviceExtension->hcd.CommonBufferVA =
FdoDeviceExtension->pDmaAdapter->DmaOperations->AllocateCommonBuffer(FdoDeviceExtension->pDmaAdapter,
PAGE_SIZE * 16,
&FdoDeviceExtension->hcd.CommonBufferPA,
FALSE);
if (FdoDeviceExtension->hcd.CommonBufferVA == 0)
{
DPRINT1("Ehci: Failed to allocate common buffer!\n");
return STATUS_UNSUCCESSFUL;
}
/* Init SpinLock for host controller device lock */
KeInitializeSpinLock(&FdoDeviceExtension->hcd.Lock);
FdoDeviceExtension->hcd.CommonBufferSize = PAGE_SIZE * 16;
/* Zeroize it */
RtlZeroMemory(FdoDeviceExtension->hcd.CommonBufferVA,
PAGE_SIZE * 16);
/* create memory allocator */
Status = DmaMemAllocator_Create(&FdoDeviceExtension->hcd.DmaMemAllocator);
if (FdoDeviceExtension->hcd.DmaMemAllocator == 0)
{
/* FIXME cleanup */
DPRINT1("Ehci: Failed to create dma memory allocator!\n");
return STATUS_UNSUCCESSFUL;
}
/* initialize memory allocator */
Status = DmaMemAllocator_Initialize(FdoDeviceExtension->hcd.DmaMemAllocator, 32, &FdoDeviceExtension->hcd.Lock, FdoDeviceExtension->hcd.CommonBufferPA, FdoDeviceExtension->hcd.CommonBufferVA, FdoDeviceExtension->hcd.CommonBufferSize);
if (!NT_SUCCESS(Status))
{
/* FIXME cleanup */
DPRINT1("Ehci: Failed to initialize dma memory allocator %x\n", Status);
return Status;
}
/* Reserved a Queue Head that will always be in the AsyncList Address Register */
FdoDeviceExtension->hcd.AsyncListQueue = CreateQueueHead(&FdoDeviceExtension->hcd);
FdoDeviceExtension->hcd.AsyncListQueue->HorizontalLinkPointer = FdoDeviceExtension->hcd.AsyncListQueue->PhysicalAddr | QH_TYPE_QH;
FdoDeviceExtension->hcd.AsyncListQueue->EndPointCharacteristics.QEDTDataToggleControl = FALSE;
FdoDeviceExtension->hcd.AsyncListQueue->Token.Bits.InterruptOnComplete = FALSE;
/* Ensure the controller is stopped */
StopEhci(&FdoDeviceExtension->hcd);
Status = IoConnectInterrupt(&FdoDeviceExtension->EhciInterrupt,
InterruptService,
DeviceObject,
NULL,
FdoDeviceExtension->Vector,
FdoDeviceExtension->Irql,
FdoDeviceExtension->Irql,
FdoDeviceExtension->Mode,
FdoDeviceExtension->IrqShared,
FdoDeviceExtension->Affinity,
FALSE);
StartEhci(&FdoDeviceExtension->hcd);
DPRINT1("AsycnAddr %x\n", GetAsyncListQueueRegister(&FdoDeviceExtension->hcd));
FdoDeviceExtension->DeviceState = DEVICESTARTED;
return STATUS_SUCCESS;
}
NTSTATUS
FdoQueryBusRelations(
PDEVICE_OBJECT DeviceObject,
PDEVICE_RELATIONS* pDeviceRelations)
{
PFDO_DEVICE_EXTENSION DeviceExtension;
PDEVICE_RELATIONS DeviceRelations = NULL;
PDEVICE_OBJECT Pdo;
PPDO_DEVICE_EXTENSION PdoDeviceExtension;
NTSTATUS Status;
ULONG UsbDeviceNumber = 0;
WCHAR CharDeviceName[64];
UNICODE_STRING DeviceName;
DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
DPRINT1("Ehci: QueryBusRelations\n");
/* Create the PDO with the next available number */
while (TRUE)
{
/* FIXME: Use safe string */
/* RtlStringCchPrintfW(CharDeviceName, 64, L"USBPDO-%d", UsbDeviceNumber); */
swprintf(CharDeviceName, L"\\Device\\USBPDO-%d", UsbDeviceNumber);
RtlInitUnicodeString(&DeviceName, CharDeviceName);
DPRINT("DeviceName %wZ\n", &DeviceName);
Status = IoCreateDevice(DeviceObject->DriverObject,
sizeof(PDO_DEVICE_EXTENSION),
&DeviceName,
FILE_DEVICE_BUS_EXTENDER,
0,
FALSE,
&Pdo);
if (NT_SUCCESS(Status))
break;
if ((Status == STATUS_OBJECT_NAME_EXISTS) || (Status == STATUS_OBJECT_NAME_COLLISION))
{
/* Try the next name */
UsbDeviceNumber++;
continue;
}
/* Bail on any other error */
if (!NT_SUCCESS(Status))
{
DPRINT1("Ehci: Failed to create PDO %wZ, Status %x\n", &DeviceName, Status);
return Status;
}
}
PdoDeviceExtension = (PPDO_DEVICE_EXTENSION)Pdo->DeviceExtension;
RtlZeroMemory(PdoDeviceExtension, sizeof(PDO_DEVICE_EXTENSION));
PdoDeviceExtension->Common.IsFdo = FALSE;
PdoDeviceExtension->ControllerFdo = DeviceObject;
PdoDeviceExtension->DeviceObject = Pdo;
PdoDeviceExtension->NumberOfPorts = DeviceExtension->hcd.ECHICaps.HCSParams.PortCount;
InitializeListHead(&PdoDeviceExtension->IrpQueue);
KeInitializeSpinLock(&PdoDeviceExtension->IrpQueueLock);
KeInitializeEvent(&PdoDeviceExtension->QueueDrainedEvent, SynchronizationEvent, TRUE);
ExInitializeFastMutex(&PdoDeviceExtension->ListLock);
Pdo->Flags &= ~DO_DEVICE_INITIALIZING;
DeviceExtension->Pdo = Pdo;
DeviceRelations = (PDEVICE_RELATIONS)ExAllocatePool(PagedPool, sizeof(DEVICE_RELATIONS));
if (!DeviceRelations)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
DeviceRelations->Count = 1;
DeviceRelations->Objects[0] = Pdo;
ObReferenceObject(Pdo);
*pDeviceRelations = DeviceRelations;
return STATUS_SUCCESS;
}
NTSTATUS NTAPI
FdoDispatchPnp(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
NTSTATUS Status;
PIO_STACK_LOCATION Stack = NULL;
PCM_PARTIAL_RESOURCE_LIST raw;
PCM_PARTIAL_RESOURCE_LIST translated;
ULONG_PTR Information = 0;
Stack = IoGetCurrentIrpStackLocation(Irp);
switch(Stack->MinorFunction)
{
case IRP_MN_START_DEVICE:
{
DPRINT1("Ehci: START_DEVICE\n");
Irp->IoStatus.Status = STATUS_SUCCESS;
Status = ForwardAndWait(DeviceObject, Irp);
raw = &Stack->Parameters.StartDevice.AllocatedResources->List[0].PartialResourceList;
translated = &Stack->Parameters.StartDevice.AllocatedResourcesTranslated->List[0].PartialResourceList;
Status = StartDevice(DeviceObject, raw, translated);
break;
}
case IRP_MN_QUERY_DEVICE_RELATIONS:
{
DPRINT1("Ehci: IRP_MN_QUERY_DEVICE_RELATIONS\n");
switch(Stack->Parameters.QueryDeviceRelations.Type)
{
case BusRelations:
{
PDEVICE_RELATIONS DeviceRelations = NULL;
DPRINT1("Ehci: BusRelations\n");
Status = FdoQueryBusRelations(DeviceObject, &DeviceRelations);
Information = (ULONG_PTR)DeviceRelations;
break;
}
default:
{
DPRINT1("Ehci: Unknown query device relations type\n");
Status = STATUS_NOT_IMPLEMENTED;
break;
}
}
break;
}
case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:
{
DPRINT1("Ehci: IRP_MN_FILTER_RESOURCE_REQUIREMENTS\n");
return ForwardIrpAndForget(DeviceObject, Irp);
break;
}
case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
{
DPRINT1("Ehci: IRP_MN_QUERY_RESOURCE_REQUIREMENTS\n");
}
case IRP_MN_QUERY_INTERFACE:
{
DPRINT1("Ehci: IRP_MN_QUERY_INTERFACE\n");
Status = STATUS_SUCCESS;
Information = 0;
Status = ForwardIrpAndForget(DeviceObject, Irp);
return Status;
break;
}
default:
{
DPRINT1("Ehci: IRP_MJ_PNP / Unhandled minor function 0x%lx\n", Stack->MinorFunction);
return ForwardIrpAndForget(DeviceObject, Irp);
}
}
Irp->IoStatus.Information = Information;
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}
NTSTATUS NTAPI
AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT PhysicalDeviceObject)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PDEVICE_OBJECT Fdo;
ULONG UsbDeviceNumber = 0;
WCHAR CharDeviceName[64];
WCHAR CharSymLinkName[64];
UNICODE_STRING DeviceName;
UNICODE_STRING SymLinkName;
UNICODE_STRING InterfaceSymLinkName;
ULONG BytesRead;
PCI_COMMON_CONFIG PciConfig;
PFDO_DEVICE_EXTENSION FdoDeviceExtension;
DPRINT1("Ehci: AddDevice\n");
/* Create the FDO with next available number */
while (TRUE)
{
/* FIXME: Use safe string sprintf*/
/* RtlStringCchPrintfW(CharDeviceName, 64, L"USBFDO-%d", UsbDeviceNumber); */
swprintf(CharDeviceName, L"\\Device\\USBFDO-%d", UsbDeviceNumber);
RtlInitUnicodeString(&DeviceName, CharDeviceName);
DPRINT("DeviceName %wZ\n", &DeviceName);
Status = IoCreateDevice(DriverObject,
sizeof(FDO_DEVICE_EXTENSION),
&DeviceName,
FILE_DEVICE_CONTROLLER,
0,
FALSE,
&Fdo);
if (NT_SUCCESS(Status))
break;
if ((Status == STATUS_OBJECT_NAME_EXISTS) || (Status == STATUS_OBJECT_NAME_COLLISION))
{
/* Try the next name */
UsbDeviceNumber++;
continue;
}
/* Bail on any other error */
if (!NT_SUCCESS(Status))
{
DPRINT1("UsbEhci: Failed to create %wZ, Status %x\n", &DeviceName, Status);
return Status;
}
}
/* FIXME */
swprintf(CharSymLinkName, L"\\Device\\HCD%d", UsbDeviceNumber);
RtlInitUnicodeString(&SymLinkName, CharSymLinkName);
Status = IoCreateSymbolicLink(&SymLinkName, &DeviceName);
if (!NT_SUCCESS(Status))
{
DPRINT1("Warning: Unable to create symbolic link for ehci host controller!\n");
}
FdoDeviceExtension = (PFDO_DEVICE_EXTENSION) Fdo->DeviceExtension;
RtlZeroMemory(FdoDeviceExtension, sizeof(FDO_DEVICE_EXTENSION));
/* initialize timer */
KeInitializeTimerEx(&FdoDeviceExtension->UpdateTimer, SynchronizationTimer);
/* initialize device extension */
FdoDeviceExtension->Common.IsFdo = TRUE;
FdoDeviceExtension->Common.DriverObject = DriverObject;
FdoDeviceExtension->Common.DeviceObject = Fdo;
FdoDeviceExtension->Pdo = PhysicalDeviceObject;
/* attach to device stack */
FdoDeviceExtension->LowerDevice = IoAttachDeviceToDeviceStack(Fdo, PhysicalDeviceObject);
if (FdoDeviceExtension->LowerDevice == NULL)
{
DPRINT1("UsbEhci: Failed to attach to device stack!\n");
IoDeleteSymbolicLink(&SymLinkName);
IoDeleteDevice(Fdo);
return STATUS_NO_SUCH_DEVICE;
}
/* setup device flags */
Fdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;
/* get bus interface */
Status = GetBusInterface(PhysicalDeviceObject, &FdoDeviceExtension->BusInterface);
/* check for success */
if (!NT_SUCCESS(Status))
{
DPRINT1("GetBusInterface() failed with %x\n", Status);
IoDetachDevice(FdoDeviceExtension->LowerDevice);
IoDeleteSymbolicLink(&SymLinkName);
IoDeleteDevice(Fdo);
return Status;
}
/* read pci config space */
BytesRead = (*FdoDeviceExtension->BusInterface.GetBusData)(
FdoDeviceExtension->BusInterface.Context,
PCI_WHICHSPACE_CONFIG,
&PciConfig,
0,
PCI_COMMON_HDR_LENGTH);
/* check for success */
if (BytesRead != PCI_COMMON_HDR_LENGTH)
{
DPRINT1("GetBusData failed!\n");
IoDetachDevice(FdoDeviceExtension->LowerDevice);
IoDeleteSymbolicLink(&SymLinkName);
IoDeleteDevice(Fdo);
return STATUS_UNSUCCESSFUL;
}
if (PciConfig.Command & PCI_ENABLE_IO_SPACE)
DPRINT("PCI_ENABLE_IO_SPACE\n");
if (PciConfig.Command & PCI_ENABLE_MEMORY_SPACE)
DPRINT("PCI_ENABLE_MEMORY_SPACE\n");
if (PciConfig.Command & PCI_ENABLE_BUS_MASTER)
DPRINT("PCI_ENABLE_BUS_MASTER\n");
DPRINT("BaseAddress[0] %x\n", PciConfig.u.type0.BaseAddresses[0]);
DPRINT1("Vendor %x\n", PciConfig.VendorID);
DPRINT1("Device %x\n", PciConfig.DeviceID);
FdoDeviceExtension->VendorId = PciConfig.VendorID;
FdoDeviceExtension->DeviceId = PciConfig.DeviceID;
/* update state */
FdoDeviceExtension->DeviceState = DEVICEINTIALIZED;
/* FIXME: delay this until IRP_MN_START arrives */
Status = IoRegisterDeviceInterface(PhysicalDeviceObject, &GUID_DEVINTERFACE_USB_HOST_CONTROLLER, NULL, &InterfaceSymLinkName);
if (!NT_SUCCESS(Status))
{
DPRINT1("Unable to register device interface!\n");
return Status;
}
else
{
/* enable device interface */
Status = IoSetDeviceInterfaceState(&InterfaceSymLinkName, TRUE);
DPRINT1("SetInterfaceState %x\n", Status);
if (!NT_SUCCESS(Status))
return Status;
}
/* device is initialized */
Fdo->Flags &= ~DO_DEVICE_INITIALIZING;
return STATUS_SUCCESS;
}
NTSTATUS NTAPI
FdoDispatchInternalDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
PFDO_DEVICE_EXTENSION FdoDeviceExtension;
PPDO_DEVICE_EXTENSION PdoDeviceExtension;
PIO_STACK_LOCATION Stack = NULL;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
ULONG_PTR Information = 0;
PUSB_DEVICE UsbDevice = NULL;
URB *Urb;
/*FIXME: This should never be called by upper drivers as they should only be dealing with the pdo */
ASSERT(0);
FdoDeviceExtension = (PFDO_DEVICE_EXTENSION) DeviceObject->DeviceExtension;
PdoDeviceExtension = (PPDO_DEVICE_EXTENSION) FdoDeviceExtension->Pdo->DeviceExtension;
ASSERT(FdoDeviceExtension->Common.IsFdo == TRUE);
Stack = IoGetCurrentIrpStackLocation(Irp);
ASSERT(Stack->Parameters.DeviceIoControl.IoControlCode == IOCTL_INTERNAL_USB_SUBMIT_URB);
Urb = (PURB) Stack->Parameters.Others.Argument1;
DPRINT("Header Length %d\n", Urb->UrbHeader.Length);
DPRINT("Header Function %d\n", Urb->UrbHeader.Function);
UsbDevice = DeviceHandleToUsbDevice(PdoDeviceExtension, Urb->UrbHeader.UsbdDeviceHandle);
if (!UsbDevice)
{
DPRINT1("Ehci: Invalid DeviceHandle or device not connected\n");
return STATUS_DEVICE_NOT_CONNECTED;
}
switch (Urb->UrbHeader.Function)
{
case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER:
{
DPRINT1("Ehci: URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER:\n");
break;
}
case URB_FUNCTION_GET_STATUS_FROM_DEVICE:
{
DPRINT1("Ehci: URB_FUNCTION_GET_STATUS_FROM_DEVICE\n");
break;
}
case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE:
{
DPRINT1("Ehci: URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE\n");
switch(Urb->UrbControlDescriptorRequest.DescriptorType)
{
case USB_DEVICE_DESCRIPTOR_TYPE:
{
DPRINT1("USB DEVICE DESC\n");
break;
}
case USB_CONFIGURATION_DESCRIPTOR_TYPE:
DPRINT1("USB CONFIG DESC\n");
//break;
case USB_STRING_DESCRIPTOR_TYPE:
{
DPRINT1("Usb String Descriptor\n");
break;
}
default:
{
DPRINT1("Ehci: Descriptor Type %x not supported!\n", Urb->UrbControlDescriptorRequest.DescriptorType);
}
}
break;
}
case URB_FUNCTION_SELECT_CONFIGURATION:
{
DPRINT1("Ehci: URB_FUNCTION_SELECT_CONFIGURATION\n");
DPRINT1("Urb->UrbSelectConfiguration.ConfigurationHandle %x\n",Urb->UrbSelectConfiguration.ConfigurationHandle);
break;
}
case URB_FUNCTION_CLASS_DEVICE:
{
DPRINT1("Ehci: URB_FUNCTION_CLASS_DEVICE %x\n",Urb->UrbControlVendorClassRequest.Request);
switch (Urb->UrbControlVendorClassRequest.Request)
{
case USB_REQUEST_GET_DESCRIPTOR:
{
DPRINT1("TransferFlags %x\n", Urb->UrbControlVendorClassRequest.TransferFlags);
DPRINT1("Urb->UrbControlVendorClassRequest.Value %x\n", Urb->UrbControlVendorClassRequest.Value);
switch (Urb->UrbControlVendorClassRequest.Value >> 8)
{
case USB_DEVICE_CLASS_AUDIO:
{
DPRINT1("USB_DEVICE_CLASS_AUDIO\n");
break;
}
case USB_DEVICE_CLASS_COMMUNICATIONS:
{
DPRINT1("USB_DEVICE_CLASS_COMMUNICATIONS\n");
break;
}
case USB_DEVICE_CLASS_HUMAN_INTERFACE:
{
DPRINT1("USB_DEVICE_CLASS_HUMAN_INTERFACE\n");
break;
}
case USB_DEVICE_CLASS_MONITOR:
{
DPRINT1("USB_DEVICE_CLASS_MONITOR\n");
break;
}
case USB_DEVICE_CLASS_PHYSICAL_INTERFACE:
{
DPRINT1("USB_DEVICE_CLASS_PHYSICAL_INTERFACE\n");
break;
}
case USB_DEVICE_CLASS_POWER:
{
DPRINT1("USB_DEVICE_CLASS_POWER\n");
break;
}
case USB_DEVICE_CLASS_PRINTER:
{
DPRINT1("USB_DEVICE_CLASS_PRINTER\n");
break;
}
case USB_DEVICE_CLASS_STORAGE:
{
DPRINT1("USB_DEVICE_CLASS_STORAGE\n");
break;
}
case USB_DEVICE_CLASS_RESERVED:
DPRINT1("Reserved!!!\n");
case USB_DEVICE_CLASS_HUB:
{
DPRINT1("USB_DEVICE_CLASS_HUB request\n");
break;
}
default:
{
DPRINT1("Unknown UrbControlVendorClassRequest Value\n");
}
}
break;
}
case USB_REQUEST_GET_STATUS:
{
DPRINT1("DEVICE: USB_REQUEST_GET_STATUS for port %d\n", Urb->UrbControlVendorClassRequest.Index);
break;
}
default:
{
DPRINT1("Unhandled URB request for class device\n");
//Urb->UrbHeader.Status = USBD_STATUS_INVALID_URB_FUNCTION;
}
}
break;
}
case URB_FUNCTION_CLASS_OTHER:
{
DPRINT1("Ehci: URB_FUNCTION_CLASS_OTHER\n");
switch (Urb->UrbControlVendorClassRequest.Request)
{
case USB_REQUEST_GET_STATUS:
{
DPRINT1("OTHER: USB_REQUEST_GET_STATUS for port %d\n", Urb->UrbControlVendorClassRequest.Index);
break;
}
case USB_REQUEST_CLEAR_FEATURE:
{
DPRINT1("USB_REQUEST_CLEAR_FEATURE Port %d, value %x\n", Urb->UrbControlVendorClassRequest.Index,
Urb->UrbControlVendorClassRequest.Value);
switch (Urb->UrbControlVendorClassRequest.Value)
{
case C_PORT_CONNECTION:
DPRINT1("Clearing Connect\n");
break;
case C_PORT_RESET:
DPRINT1("Clearing Reset\n");
break;
default:
DPRINT1("Unknown Value for Clear Feature %x \n", Urb->UrbControlVendorClassRequest.Value);
break;
}
break;
}
case USB_REQUEST_SET_FEATURE:
{
DPRINT1("USB_REQUEST_SET_FEATURE Port %d, value %x\n", Urb->UrbControlVendorClassRequest.Index,
Urb->UrbControlVendorClassRequest.Value);
switch(Urb->UrbControlVendorClassRequest.Value)
{
case PORT_RESET:
{
DPRINT1("Port reset\n");
break;
}
case PORT_ENABLE:
{
DPRINT1("Unhandled Set Feature\n");
break;
}
default:
{
DPRINT1("Unknown Set Feature!\n");
break;
}
}
break;
}
case USB_REQUEST_SET_ADDRESS:
{
DPRINT1("USB_REQUEST_SET_ADDRESS\n");
break;
}
case USB_REQUEST_GET_DESCRIPTOR:
{
DPRINT1("USB_REQUEST_GET_DESCRIPTOR\n");
break;
}
case USB_REQUEST_SET_DESCRIPTOR:
{
DPRINT1("USB_REQUEST_SET_DESCRIPTOR\n");
break;
}
case USB_REQUEST_GET_CONFIGURATION:
{
DPRINT1("USB_REQUEST_GET_CONFIGURATION\n");
break;
}
case USB_REQUEST_SET_CONFIGURATION:
{
DPRINT1("USB_REQUEST_SET_CONFIGURATION\n");
break;
}
case USB_REQUEST_GET_INTERFACE:
{
DPRINT1("USB_REQUEST_GET_INTERFACE\n");
break;
}
case USB_REQUEST_SET_INTERFACE:
{
DPRINT1("USB_REQUEST_SET_INTERFACE\n");
break;
}
case USB_REQUEST_SYNC_FRAME:
{
DPRINT1("USB_REQUEST_SYNC_FRAME\n");
break;
}
default:
{
DPRINT1("Unknown Function Class Unknown request\n");
break;
}
}
break;
}
default:
{
DPRINT1("Ehci: Unhandled URB %x\n", Urb->UrbHeader.Function);
//Urb->UrbHeader.Status = USBD_STATUS_INVALID_URB_FUNCTION;
}
}
Irp->IoStatus.Information = Information;
if (Status != STATUS_PENDING)
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}