mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4993 lines
131 KiB
4993 lines
131 KiB
/*++
|
|
|
|
Copyright (c) 1995,1996 Microsoft Corporation
|
|
:ts=4
|
|
|
|
Module Name:
|
|
|
|
uhcd.c
|
|
|
|
Abstract:
|
|
|
|
The UHC driver for USB, this module contains the initialization code.
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Notes:
|
|
|
|
Revision History:
|
|
|
|
10-08-95 : created
|
|
|
|
--*/
|
|
|
|
#include "wdm.h"
|
|
#include <windef.h>
|
|
#include <unknown.h>
|
|
#ifdef DRM_SUPPORT
|
|
#include <ks.h>
|
|
#include <ksmedia.h>
|
|
#include <drmk.h>
|
|
#include <ksdrmhlp.h>
|
|
#endif
|
|
#include "stdarg.h"
|
|
#include "stdio.h"
|
|
|
|
#include "usbdi.h"
|
|
#include "hcdi.h"
|
|
#include "uhcd.h"
|
|
|
|
#ifdef DRM_SUPPORT
|
|
NTSTATUS
|
|
UHCD_PreUSBD_SetContentId
|
|
(
|
|
IN PIRP irp,
|
|
IN PKSP_DRMAUDIOSTREAM_CONTENTID pKsProperty,
|
|
IN PKSDRMAUDIOSTREAM_CONTENTID pvData
|
|
);
|
|
|
|
NTSTATUS
|
|
UHCD_PostUSBD_SetContentId
|
|
(
|
|
IN PIRP irp,
|
|
IN PKSP_DRMAUDIOSTREAM_CONTENTID pKsProperty,
|
|
IN PKSDRMAUDIOSTREAM_CONTENTID pvData
|
|
);
|
|
|
|
#endif
|
|
|
|
#ifdef PAGE_CODE
|
|
#ifdef ALLOC_PRAGMA
|
|
// WIN98 breaks if we have an INIT segment
|
|
//#pragma alloc_text(INIT, DriverEntry)
|
|
#pragma alloc_text(PAGE, UHCD_CreateDeviceObject)
|
|
#pragma alloc_text(PAGE, UHCD_ReadWriteConfig)
|
|
#pragma alloc_text(PAGE, UHCD_QueryCapabilities)
|
|
#pragma alloc_text(PAGE, UHCD_StartDevice)
|
|
#pragma alloc_text(PAGE, UHCD_InitializeSchedule)
|
|
#pragma alloc_text(PAGE, UHCD_StartGlobalReset)
|
|
#pragma alloc_text(PAGE, UHCD_Suspend)
|
|
#pragma alloc_text(PAGE, UHCD_StopBIOS)
|
|
#ifdef DRM_SUPPORT
|
|
#pragma alloc_text(PAGE, UHCD_PreUSBD_SetContentId)
|
|
#pragma alloc_text(PAGE, UHCD_PostUSBD_SetContentId)
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
NTSTATUS
|
|
DriverEntry(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING RegistryPath
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Installable driver initialization entry point.
|
|
This entry point is called directly by the I/O system.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - pointer to the driver object
|
|
|
|
RegistryPath - pointer to a unicode string representing the path
|
|
to driver-specific key in the registry
|
|
|
|
Return Value:
|
|
|
|
NT status code
|
|
|
|
--*/
|
|
{
|
|
|
|
PDEVICE_OBJECT deviceObject = NULL;
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
|
|
UHCD_KdPrint((2, "'entering DriverEntry\n"));
|
|
|
|
UHCD_KdPrint ((1, "'UHCI Universal Serial Bus Host Controller Driver.\n"));
|
|
UHCD_KdPrint ((1, "'HCD using USBDI version %x\n", USBDI_VERSION));
|
|
|
|
#ifdef DEBUG_LOG
|
|
//
|
|
// Initialize our debug trace log
|
|
//
|
|
UHCD_LogInit();
|
|
#endif
|
|
|
|
//
|
|
// Create dispatch points for device control, create, close.
|
|
//
|
|
|
|
DriverObject->MajorFunction[IRP_MJ_CREATE]=
|
|
DriverObject->MajorFunction[IRP_MJ_CLOSE] =
|
|
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
|
|
DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] =
|
|
DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = UHCD_Dispatch;
|
|
|
|
DriverObject->MajorFunction[IRP_MJ_PNP] = UHCD_Dispatch;
|
|
DriverObject->MajorFunction[IRP_MJ_POWER] = UHCD_Dispatch;
|
|
|
|
DriverObject->DriverExtension->AddDevice = UHCD_PnPAddDevice;
|
|
|
|
DriverObject->DriverUnload = UHCD_Unload;
|
|
DriverObject->DriverStartIo = UHCD_StartIo;
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
UHCD_ProcessPnPIrp(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Process the Power IRPs sent to the PDO for this device.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to a hcd device object (FDO)
|
|
|
|
Irp - pointer to an I/O Request Packet
|
|
|
|
Return Value:
|
|
|
|
NT status code
|
|
|
|
--*/
|
|
{
|
|
|
|
PIO_STACK_LOCATION irpStack;
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
PDEVICE_OBJECT stackDeviceObject;
|
|
BOOLEAN touchTheHardware = TRUE;
|
|
|
|
UHCD_KdPrint((2, "'IRP_MJ_PNP\n"));
|
|
|
|
// we should only process PnP messages sent to our
|
|
// FDO for the HCD, USBD will handle any others.
|
|
// UHCD_ASSERT(DeviceObject == hcdDeviceObject);
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
deviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
UHCD_PrintPnPMessage("PNP DISPATCH:", irpStack->MinorFunction);
|
|
UHCD_ASSERT(irpStack->MajorFunction == IRP_MJ_PNP);
|
|
|
|
switch (irpStack->MinorFunction) {
|
|
|
|
case IRP_MN_START_DEVICE:
|
|
|
|
//
|
|
// USB handles start for us so we
|
|
// should not get here.
|
|
//
|
|
|
|
LOGENTRY(LOG_MISC, 'STR!', deviceExtension->TopOfStackDeviceObject, 0, 0);
|
|
UHCD_KdTrap(("HCD START_DEVICE Irp\n"));
|
|
break;
|
|
|
|
//
|
|
// STOP & REMOVE messages unload the driver
|
|
// when we get a STOP message it is still possible
|
|
// touch the hardware, when we get a REMOVE message
|
|
// we have to assume that the hardware is gone.
|
|
//
|
|
|
|
case IRP_MN_STOP_DEVICE:
|
|
|
|
stackDeviceObject = deviceExtension->TopOfStackDeviceObject;
|
|
|
|
ntStatus = UHCD_StopDevice(DeviceObject);
|
|
|
|
UHCD_CleanupDevice(DeviceObject);
|
|
|
|
LOGENTRY(LOG_MISC, 'STOP', deviceExtension->TopOfStackDeviceObject, 0, 0);
|
|
// Pass on to PDO
|
|
break;
|
|
|
|
case IRP_MN_SURPRISE_REMOVAL:
|
|
touchTheHardware = FALSE;
|
|
LOGENTRY(LOG_MISC, 'SRMV', deviceExtension->TopOfStackDeviceObject,
|
|
ntStatus, 0);
|
|
|
|
case IRP_MN_REMOVE_DEVICE:
|
|
|
|
stackDeviceObject = deviceExtension->TopOfStackDeviceObject;
|
|
|
|
//
|
|
// BUGBUG
|
|
// we really only want stop processing if we are
|
|
// sure the device is present.
|
|
//
|
|
|
|
ntStatus = UHCD_StopDevice(DeviceObject);
|
|
|
|
UHCD_CleanupDevice(DeviceObject);
|
|
|
|
LOGENTRY(LOG_MISC, 'REMV', deviceExtension->TopOfStackDeviceObject,
|
|
ntStatus, 0);
|
|
|
|
//
|
|
// Undo anything we did in the PnPAddDevice function
|
|
//
|
|
|
|
UHCD_KdPrint((2, "'UHCD -- removing device object\n"));
|
|
|
|
//
|
|
// Detach FDO from PDO
|
|
//
|
|
|
|
IoCopyCurrentIrpStackLocationToNext(Irp);
|
|
|
|
//
|
|
// pass on to our PDO
|
|
//
|
|
|
|
ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject,
|
|
Irp);
|
|
|
|
//
|
|
// important to detach after we pass the irp on
|
|
//
|
|
|
|
IoDetachDevice( deviceExtension->TopOfStackDeviceObject );
|
|
|
|
USBD_FreeDeviceName(deviceExtension->DeviceNameHandle);
|
|
|
|
//
|
|
// Delete the device object we created for this controller
|
|
//
|
|
|
|
IoDeleteDevice (DeviceObject);
|
|
goto UHCD_ProcessPnPIrp_Done;
|
|
|
|
break;
|
|
|
|
//
|
|
// All other PnP messages passed on to our PDO
|
|
//
|
|
|
|
default:
|
|
stackDeviceObject = deviceExtension->TopOfStackDeviceObject;
|
|
UHCD_ASSERT(stackDeviceObject != NULL);
|
|
UHCD_KdPrint((2, "'UNKNOWN PNP MESSAGE (%x)\n", irpStack->MinorFunction));
|
|
|
|
//
|
|
// All unahndled PnP messages are passed on to the PDO
|
|
//
|
|
|
|
} /* case PNP minor function */
|
|
|
|
IoCopyCurrentIrpStackLocationToNext(Irp);
|
|
|
|
//
|
|
// pass on to our PDO
|
|
//
|
|
|
|
ntStatus = IoCallDriver(stackDeviceObject,
|
|
Irp);
|
|
|
|
UHCD_ProcessPnPIrp_Done:
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
#ifdef DRM_SUPPORT
|
|
|
|
NTSTATUS
|
|
UHCD_PreUSBD_SetContentId
|
|
(
|
|
IN PIRP irp,
|
|
IN PKSP_DRMAUDIOSTREAM_CONTENTID pKsProperty,
|
|
IN PKSDRMAUDIOSTREAM_CONTENTID pvData
|
|
)
|
|
/* ++
|
|
*
|
|
* Description:
|
|
*
|
|
*
|
|
* Arguments:
|
|
*
|
|
* Return:
|
|
*
|
|
* -- */
|
|
{
|
|
PVOID Handlers[1];
|
|
ULONG ContentId;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(irp);
|
|
ASSERT(pKsProperty);
|
|
ASSERT(pvData);
|
|
|
|
ContentId = pvData->ContentId;
|
|
Handlers[0] = USBD_Dispatch;
|
|
return pKsProperty->DrmAddContentHandlers(ContentId, Handlers, SIZEOF_ARRAY(Handlers));
|
|
}
|
|
|
|
NTSTATUS
|
|
UHCD_PostUSBD_SetContentId
|
|
(
|
|
IN PIRP irp,
|
|
IN PKSP_DRMAUDIOSTREAM_CONTENTID pKsProperty,
|
|
IN PKSDRMAUDIOSTREAM_CONTENTID pvData
|
|
)
|
|
/* ++
|
|
*
|
|
* Description:
|
|
*
|
|
*
|
|
* Arguments:
|
|
*
|
|
* Return:
|
|
*
|
|
* -- */
|
|
{
|
|
NTSTATUS ntStatus;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(irp);
|
|
ASSERT(pKsProperty);
|
|
ASSERT(pvData);
|
|
|
|
// ioStackLocation = IoGetCurrentIrpStackLocation(irp);
|
|
// deviceExtension = ioStackLocation->DeviceObject->DeviceExtension;
|
|
// Context = pKsProperty->Context;
|
|
// ContentId = pvData->ContentId;;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
NTSTATUS
|
|
UHCD_Dispatch(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Process the IRPs sent to this device.
|
|
|
|
Power States for the USB host controller
|
|
D0 - On.
|
|
D1 - USB defined Suspend per 1.00 specification.
|
|
D2 - undefined, reserved for future use.
|
|
D3 - Off
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to a device object
|
|
|
|
Irp - pointer to an I/O Request Packet
|
|
|
|
Return Value:
|
|
|
|
NT status code
|
|
|
|
--*/
|
|
{
|
|
|
|
PIO_STACK_LOCATION irpStack;
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
PDEVICE_OBJECT hcdDeviceObject;
|
|
|
|
UHCD_KdPrint((2, "'enter UHCD_Dispatch\n"));
|
|
|
|
#ifdef DRM_SUPPORT
|
|
|
|
//
|
|
// Need to check DRM request before passing to USBD and advise DRM of
|
|
// the USBD entry point. Otherwise, a rogue USBD could circumvent DRM.
|
|
//
|
|
irpStack = IoGetCurrentIrpStackLocation (Irp);
|
|
if (IRP_MJ_DEVICE_CONTROL == irpStack->MajorFunction && IOCTL_KS_PROPERTY == irpStack->Parameters.DeviceIoControl.IoControlCode) {
|
|
NTSTATUS ntStatus;
|
|
ntStatus = KsPropertyHandleDrmSetContentId(Irp, UHCD_PreUSBD_SetContentId);
|
|
if (!NT_SUCCESS(ntStatus) && ntStatus != STATUS_PROPSET_NOT_FOUND) {
|
|
Irp->IoStatus.Status = ntStatus;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return ntStatus;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
//
|
|
// pass the irp to USBD
|
|
//
|
|
if (!USBD_Dispatch(DeviceObject,
|
|
Irp,
|
|
&hcdDeviceObject,
|
|
&ntStatus)) {
|
|
//
|
|
// Irp was completed by USBD just exit our dispatch
|
|
// routine.
|
|
//
|
|
goto UHCD_Dispatch_Done;
|
|
}
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION) hcdDeviceObject->DeviceExtension;
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation (Irp);
|
|
|
|
UHCD_KdPrint((2, "'UHCD_Dispatch IRP = %x, stack = %x\n", Irp, irpStack));
|
|
|
|
switch (irpStack->MajorFunction) {
|
|
|
|
case IRP_MJ_CREATE:
|
|
|
|
UHCD_KdPrint((2, "'IRP_MJ_CREATE\n"));
|
|
UHCD_CompleteIrp(hcdDeviceObject, Irp, ntStatus, 0, NULL);
|
|
|
|
break;
|
|
|
|
case IRP_MJ_CLOSE:
|
|
|
|
UHCD_KdPrint((2, "'IRP_MJ_CLOSE\n"));
|
|
UHCD_CompleteIrp(hcdDeviceObject, Irp, ntStatus, 0, NULL);
|
|
|
|
break;
|
|
|
|
case IRP_MJ_INTERNAL_DEVICE_CONTROL:
|
|
UHCD_KdPrint((2, "'IRP_MJ_INTERNAL_DEVICE_CONTROL\n"));
|
|
|
|
switch (irpStack->Parameters.DeviceIoControl.IoControlCode) {
|
|
|
|
case IOCTL_INTERNAL_USB_SUBMIT_URB:
|
|
|
|
UHCD_KdPrint((2, "'IOCTL_USB_SUBMIT_URB\n"));
|
|
ntStatus = UHCD_URB_Dispatch(hcdDeviceObject, Irp);
|
|
|
|
break;
|
|
default:
|
|
|
|
// this IOCTL not handled by the HCD, we need
|
|
// to invetigate why
|
|
UHCD_KdTrap(("why is this IOCTL NOT HANDLED by HCD?\n"));
|
|
|
|
// BUGBUG
|
|
UHCD_CompleteIrp(hcdDeviceObject, Irp, STATUS_SUCCESS, 0, NULL);
|
|
break;
|
|
} /* case */
|
|
|
|
break;
|
|
|
|
case IRP_MJ_DEVICE_CONTROL:
|
|
UHCD_KdPrint((2, "'IRP_MJ_DEVICE_CONTROL\n"));
|
|
|
|
switch (irpStack->Parameters.DeviceIoControl.IoControlCode) {
|
|
|
|
#ifdef DRM_SUPPORT
|
|
|
|
case IOCTL_KS_PROPERTY:
|
|
{
|
|
ntStatus = KsPropertyHandleDrmSetContentId(Irp, UHCD_PostUSBD_SetContentId);
|
|
UHCD_CompleteIrp(hcdDeviceObject, Irp, ntStatus, 0, NULL);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
case IOCTL_USB_HCD_GET_STATS_1:
|
|
{
|
|
PVOID ioBuffer;
|
|
ULONG inputBufferLength;
|
|
ULONG outputBufferLength;
|
|
PHCD_STAT_INFORMATION_1 uhcdStatInfo;
|
|
|
|
UHCD_KdPrint((1, "'IOCTL_USB_HCD_GET_STATS 1\n"));
|
|
|
|
//
|
|
// Get a pointer to the current location in the Irp. This is where
|
|
// the function codes and parameters are located.
|
|
//
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation (Irp);
|
|
|
|
//
|
|
// Get the pointer to the input/output buffer and it's length
|
|
//
|
|
|
|
uhcdStatInfo = (PHCD_STAT_INFORMATION_1) ioBuffer = Irp->AssociatedIrp.SystemBuffer;
|
|
inputBufferLength = irpStack->Parameters.DeviceIoControl.InputBufferLength;
|
|
outputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
|
|
|
|
if (outputBufferLength >= sizeof(HCD_STAT_INFORMATION_1)) {
|
|
|
|
//
|
|
// return the IOBASE address of the controller
|
|
// followed by the Phys address of the persistent queue head
|
|
//
|
|
// This is for Intels TD-Poker app on Memphis
|
|
#ifdef NTKERN
|
|
uhcdStatInfo->Reserved1 =
|
|
*((PULONG) &deviceExtension->DeviceRegisters[0]);
|
|
uhcdStatInfo->Reserved2 =
|
|
(ULONG) deviceExtension->PersistantQueueHead->PhysicalAddress;
|
|
#endif
|
|
// reg counters
|
|
RtlCopyMemory(&uhcdStatInfo->Counters,
|
|
&deviceExtension->Stats,
|
|
sizeof(uhcdStatInfo->Counters));
|
|
|
|
|
|
KeQuerySystemTime(&uhcdStatInfo->TimeRead);
|
|
|
|
if (uhcdStatInfo->ResetCounters) {
|
|
UHCD_KdPrint((1, "'<Reset Stats>\n"));
|
|
RtlZeroMemory(&deviceExtension->Stats,
|
|
sizeof(deviceExtension->Stats));
|
|
}
|
|
ntStatus = STATUS_SUCCESS;
|
|
} else {
|
|
ntStatus = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
UHCD_KdPrint((2, "'inputBufferLength = %d outputBufferLength = %d\n",
|
|
inputBufferLength, outputBufferLength));
|
|
|
|
UHCD_CompleteIrp(hcdDeviceObject, Irp, ntStatus,
|
|
sizeof(HCD_STAT_INFORMATION_1), NULL);
|
|
}
|
|
break;
|
|
|
|
|
|
|
|
case IOCTL_USB_HCD_GET_STATS_2:
|
|
{
|
|
PVOID ioBuffer;
|
|
ULONG inputBufferLength;
|
|
ULONG outputBufferLength;
|
|
PHCD_STAT_INFORMATION_2 uhcdStatInfo;
|
|
|
|
UHCD_KdPrint((1, "'IOCTL_USB_HCD_GET_STATS 2\n"));
|
|
|
|
//
|
|
// Get a pointer to the current location in the Irp. This is where
|
|
// the function codes and parameters are located.
|
|
//
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation (Irp);
|
|
|
|
//
|
|
// Get the pointer to the input/output buffer and it's length
|
|
//
|
|
|
|
uhcdStatInfo = (PHCD_STAT_INFORMATION_2) ioBuffer = Irp->AssociatedIrp.SystemBuffer;
|
|
inputBufferLength = irpStack->Parameters.DeviceIoControl.InputBufferLength;
|
|
outputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
|
|
|
|
if (outputBufferLength >= sizeof(HCD_STAT_INFORMATION_2)) {
|
|
extern LONG UHCD_CommonBufferBytes;
|
|
//
|
|
// return the IOBASE address of the controller
|
|
// followed by the Phys address of the persistent queue head
|
|
//
|
|
// This is for Intels TD-Poker app on Memphis
|
|
#ifdef NTKERN
|
|
uhcdStatInfo->Reserved1 =
|
|
*((PULONG) &deviceExtension->DeviceRegisters[0]);
|
|
uhcdStatInfo->Reserved2 =
|
|
(ULONG) deviceExtension->PersistantQueueHead->PhysicalAddress;
|
|
#endif
|
|
|
|
// reg counters
|
|
RtlCopyMemory(&uhcdStatInfo->Counters,
|
|
&deviceExtension->Stats,
|
|
sizeof(uhcdStatInfo->Counters));
|
|
|
|
|
|
// iso counters
|
|
RtlCopyMemory(&uhcdStatInfo->IsoCounters,
|
|
&deviceExtension->IsoStats,
|
|
sizeof(uhcdStatInfo->IsoCounters));
|
|
|
|
KeQuerySystemTime(&uhcdStatInfo->TimeRead);
|
|
|
|
uhcdStatInfo->LockedMemoryUsed = UHCD_CommonBufferBytes;
|
|
|
|
|
|
if (uhcdStatInfo->ResetCounters) {
|
|
UHCD_KdPrint((1, "'<Reset Stats>\n"));
|
|
RtlZeroMemory(&deviceExtension->Stats,
|
|
sizeof(deviceExtension->Stats));
|
|
RtlZeroMemory(&deviceExtension->IsoStats,
|
|
sizeof(deviceExtension->IsoStats));
|
|
deviceExtension->LastFrameInterrupt = 0;
|
|
}
|
|
ntStatus = STATUS_SUCCESS;
|
|
} else {
|
|
ntStatus = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
UHCD_KdPrint((2, "'inputBufferLength = %d outputBufferLength = %d\n",
|
|
inputBufferLength, outputBufferLength));
|
|
|
|
UHCD_CompleteIrp(hcdDeviceObject, Irp, ntStatus,
|
|
sizeof(HCD_STAT_INFORMATION_2), NULL);
|
|
}
|
|
break;
|
|
|
|
|
|
#if DBG
|
|
//
|
|
// This is a test IOCTL only in debug versions
|
|
//
|
|
|
|
case IOCTL_USB_HCD_DISABLE_PORT:
|
|
{
|
|
PVOID ioBuffer;
|
|
ULONG inputBufferLength;
|
|
ULONG outputBufferLength;
|
|
PIO_STACK_LOCATION pIrpSp;
|
|
ULONG portIndex;
|
|
PROOTHUB pRootHub;
|
|
|
|
pIrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
ioBuffer = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
pRootHub = deviceExtension->RootHub;
|
|
|
|
inputBufferLength
|
|
= pIrpSp->Parameters.DeviceIoControl.InputBufferLength;
|
|
|
|
if (inputBufferLength < sizeof(ULONG)) {
|
|
ntStatus = STATUS_BUFFER_TOO_SMALL;
|
|
goto IoctlDisablePortError;
|
|
}
|
|
|
|
portIndex = *(ULONG *)ioBuffer;
|
|
|
|
if (portIndex >= pRootHub->NumberOfPorts) {
|
|
ntStatus = STATUS_INVALID_PARAMETER;
|
|
goto IoctlDisablePortError;
|
|
}
|
|
|
|
//
|
|
// Flag the port as having no devices in there and
|
|
// status changed.
|
|
//
|
|
|
|
pRootHub->DisabledPort[portIndex]
|
|
|= UHCD_FAKE_CONNECT_CHANGE | UHCD_FAKE_DISCONNECT;
|
|
|
|
IoctlDisablePortError:;
|
|
|
|
//
|
|
// Complete the IRP
|
|
//
|
|
|
|
UHCD_CompleteIrp(hcdDeviceObject, Irp, ntStatus, 0, NULL);
|
|
}
|
|
break;
|
|
|
|
case IOCTL_USB_HCD_ENABLE_PORT:
|
|
{
|
|
PVOID ioBuffer;
|
|
ULONG inputBufferLength;
|
|
ULONG outputBufferLength;
|
|
PIO_STACK_LOCATION pIrpSp;
|
|
ULONG portIndex;
|
|
PROOTHUB pRootHub;
|
|
|
|
pIrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
ioBuffer = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
pRootHub = deviceExtension->RootHub;
|
|
|
|
inputBufferLength
|
|
= pIrpSp->Parameters.DeviceIoControl.InputBufferLength;
|
|
|
|
if (inputBufferLength < sizeof(ULONG)) {
|
|
ntStatus = STATUS_BUFFER_TOO_SMALL;
|
|
goto IoctlEnablePortError;
|
|
}
|
|
|
|
portIndex = *(ULONG *)ioBuffer;
|
|
|
|
if (portIndex >= pRootHub->NumberOfPorts) {
|
|
ntStatus = STATUS_INVALID_PARAMETER;
|
|
goto IoctlEnablePortError;
|
|
}
|
|
|
|
//
|
|
// Flag the port as having no devices in there and
|
|
// status changed.
|
|
//
|
|
|
|
pRootHub->DisabledPort[portIndex] = UHCD_FAKE_CONNECT_CHANGE;
|
|
|
|
IoctlEnablePortError:;
|
|
|
|
//
|
|
// Complete the IRP
|
|
//
|
|
|
|
UHCD_CompleteIrp(hcdDeviceObject, Irp, ntStatus, 0, NULL);
|
|
}
|
|
break;
|
|
|
|
#endif
|
|
|
|
default:
|
|
ntStatus = STATUS_INVALID_DEVICE_REQUEST;
|
|
|
|
UHCD_CompleteIrp(hcdDeviceObject, Irp,
|
|
STATUS_INVALID_DEVICE_REQUEST, 0, NULL);
|
|
|
|
|
|
} /* case */
|
|
|
|
break;
|
|
|
|
//
|
|
// Process PnP and Power messages
|
|
//
|
|
case IRP_MJ_POWER:
|
|
// should not get here
|
|
UHCD_KdTrap(("Power Message to HCD\n"));
|
|
break;
|
|
|
|
case IRP_MJ_PNP:
|
|
|
|
ntStatus = UHCD_ProcessPnPIrp(hcdDeviceObject, Irp);
|
|
break;
|
|
|
|
default:
|
|
UHCD_KdPrint((2, "'unrecognized IRP_MJ_ function (%x)\n", irpStack->MajorFunction));
|
|
ntStatus = STATUS_INVALID_PARAMETER;
|
|
UHCD_CompleteIrp(hcdDeviceObject, Irp, ntStatus, 0, NULL);
|
|
} /* case MJ_FUNCTION */
|
|
|
|
UHCD_Dispatch_Done:
|
|
|
|
UHCD_KdPrint((2, "'exit UHCD_Dispatch 0x%x\n", ntStatus));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
UHCD_SetDevicePowerState(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN DEVICE_POWER_STATE DeviceState
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the device object for the class device.
|
|
|
|
Irp - Irp completed.
|
|
|
|
DeviceState - Device specific power state to set the device in to.
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
BOOLEAN hookIt = FALSE;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PUSBD_EXTENSION usbdExtension;
|
|
PDEVICE_CAPABILITIES hcDeviceCapabilities;
|
|
BOOLEAN bIsSuspend;
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
irpStack = IoGetCurrentIrpStackLocation (Irp);
|
|
|
|
if (irpStack->Parameters.Power.Type ==
|
|
SystemPowerState) {
|
|
|
|
switch (irpStack->Parameters.Power.State.SystemState) {
|
|
case PowerSystemSleeping1:
|
|
case PowerSystemSleeping2:
|
|
case PowerSystemSleeping3:
|
|
|
|
// suspend coming thru
|
|
UHCD_KdPrint((1, "'Shutdown (Suspend) Host Controller\n"));
|
|
deviceExtension->HcFlags |= HCFLAG_SUSPEND_NEXT_D3;
|
|
|
|
ntStatus = STATUS_SUCCESS;
|
|
break;
|
|
|
|
case PowerSystemShutdown:
|
|
|
|
//
|
|
// this is a shutdown request
|
|
//
|
|
|
|
UHCD_KdPrint((1, "'Shutdown Host Controller\n"));
|
|
|
|
//
|
|
// do a stop to unhook the interrupt
|
|
//
|
|
UHCD_StopDevice(DeviceObject);
|
|
|
|
#ifdef NTKERN
|
|
//
|
|
// now give control back to the BIOS if we have one
|
|
//
|
|
if (deviceExtension->HcFlags & HCFLAG_USBBIOS) {
|
|
UHCD_StartBIOS(DeviceObject);
|
|
}
|
|
#endif
|
|
|
|
ntStatus = STATUS_SUCCESS;
|
|
break;
|
|
|
|
default:
|
|
// should not get here
|
|
TRAP();
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
bIsSuspend = (deviceExtension->HcFlags & HCFLAG_SUSPEND_NEXT_D3) ? 1:0;
|
|
deviceExtension->HcFlags &= ~HCFLAG_SUSPEND_NEXT_D3;
|
|
|
|
switch (DeviceState) {
|
|
case PowerDeviceD3:
|
|
//
|
|
// Request for HC to power off
|
|
// we will:
|
|
//
|
|
// 1. stop the controller schedule
|
|
// 2. clean up the schedule
|
|
// 3. reset the frame counter
|
|
//
|
|
|
|
LOGENTRY(LOG_MISC, 'D3go', deviceExtension, 0, 0);
|
|
|
|
// it is possible (although remote) to are get a D3 with no
|
|
// root hub attached if so we will turn off the hardware here
|
|
if (!(deviceExtension->HcFlags & HCFLAG_RH_OFF)) {
|
|
|
|
UHCD_SaveHCstate(DeviceObject);
|
|
UHCD_Suspend(DeviceObject, FALSE);
|
|
}
|
|
|
|
UHCD_KdPrint((2, "'PowerDeviceD3 (OFF)\n"));
|
|
|
|
// In the NT power management model, D3 is not necessarily "OFF".
|
|
// What governs this is the DeviceWake setting in the DeviceCaps
|
|
// structure. If DeviceWake for our controller device is D3, then
|
|
// we know that it is possible for the controller to wake the
|
|
// machine from this power level. The controller must have power
|
|
// to be able to do so, therefore, we suppress setting the
|
|
// HCFLAG_LOST_POWER flag in this case. Setting it actually has
|
|
// the undesired effect of causing us to reset the controller on
|
|
// resume, which in turn causes the hub to fail and the devices to
|
|
// be surprise removed/reenumerated unnecessarily when the hub is
|
|
// reinitialized. This normally isn't more than a minor annoyance
|
|
// (e.g. slow resume time), except in the case where one of these
|
|
// devices is a USB mass storage device. Surprise removal is
|
|
// dangerous for mass storage devices, and the user is presented
|
|
// with the annoying "don't surprise remove this device" dialog
|
|
// when the system is resumed, even though the user himself did not
|
|
// directly cause the device removal.
|
|
//
|
|
// Note that the case where the host controller really does lose
|
|
// power could result in the same problem, but that will have to
|
|
// be addressed in the hub driver.
|
|
|
|
usbdExtension = (PUSBD_EXTENSION)deviceExtension;
|
|
hcDeviceCapabilities = &usbdExtension->HcDeviceCapabilities;
|
|
if (!bIsSuspend ||
|
|
DeviceState > hcDeviceCapabilities->DeviceWake) {
|
|
deviceExtension->HcFlags |= HCFLAG_LOST_POWER;
|
|
UHCD_KdPrint((1, "'HC will lose power in D3\n"));
|
|
}
|
|
#if DBG
|
|
else {
|
|
UHCD_KdPrint((1, "'HC will NOT lose power in D3\n"));
|
|
}
|
|
#endif
|
|
|
|
// ensure no interrupts are generated by the controller
|
|
{
|
|
USHORT legsup;
|
|
|
|
UHCD_ReadWriteConfig(deviceExtension->PhysicalDeviceObject,
|
|
TRUE,
|
|
&legsup,
|
|
0xc0, // offset of legacy bios reg
|
|
sizeof(legsup));
|
|
|
|
LOGENTRY(LOG_MISC, 'PIRd', deviceExtension, legsup, 0);
|
|
// clear the PIRQD routing bit
|
|
legsup &= ~LEGSUP_USBPIRQD_EN;
|
|
|
|
UHCD_ReadWriteConfig(deviceExtension->PhysicalDeviceObject,
|
|
FALSE,
|
|
&legsup,
|
|
0xc0, // offset of legacy bios reg
|
|
sizeof(legsup));
|
|
}
|
|
|
|
|
|
deviceExtension->CurrentDevicePowerState = DeviceState;
|
|
UHCD_KdPrint((1, "'Host Controller entered (D%d)\n", DeviceState-1));
|
|
|
|
// pass on to PDO
|
|
break;
|
|
|
|
case PowerDeviceD1:
|
|
case PowerDeviceD2:
|
|
//
|
|
// power states D1,D2 translate to USB suspend
|
|
|
|
UHCD_KdPrint((2, "'PowerDeviceD1/D2 (SUSPEND) HC\n"));
|
|
#ifdef DEBUG_LOG
|
|
if (DeviceState == PowerDeviceD1) {
|
|
LOGENTRY(LOG_MISC, 'D1go', deviceExtension, 0, 0);
|
|
} else {
|
|
LOGENTRY(LOG_MISC, 'D2go', deviceExtension, 0, 0);
|
|
}
|
|
#endif
|
|
|
|
// change the state of the PRIQD routing bit
|
|
{
|
|
USHORT legsup;
|
|
|
|
UHCD_ReadWriteConfig( deviceExtension->PhysicalDeviceObject,
|
|
TRUE,
|
|
&legsup,
|
|
0xc0, // offset of legacy bios reg
|
|
sizeof(legsup));
|
|
|
|
LOGENTRY(LOG_MISC, 'PIRd', deviceExtension, legsup, 0);
|
|
// clear the PIRQD routing bit
|
|
legsup &= ~LEGSUP_USBPIRQD_EN;
|
|
|
|
UHCD_ReadWriteConfig( deviceExtension->PhysicalDeviceObject,
|
|
FALSE,
|
|
&legsup,
|
|
0xc0, // offset of legacy bios reg
|
|
sizeof(legsup));
|
|
}
|
|
|
|
//
|
|
// Note, we should not get here unless all the children of the HC
|
|
// have been suspended.
|
|
//
|
|
|
|
deviceExtension->CurrentDevicePowerState = DeviceState;
|
|
UHCD_KdPrint((1, "'Host Controller entered (D%d)\n", DeviceState-1));
|
|
|
|
// pass on to PDO
|
|
break;
|
|
|
|
case PowerDeviceD0:
|
|
|
|
//
|
|
// Request for HC to go to resume
|
|
// we will:
|
|
//
|
|
// 1. start the controller in the completetion routine
|
|
//
|
|
|
|
UHCD_KdPrint((2, "'PowerDeviceD0 (ON), defer\n"));
|
|
LOGENTRY(LOG_MISC, 'D0go', deviceExtension, 0, 0);
|
|
|
|
//
|
|
// finish the rest in the completion routine
|
|
//
|
|
|
|
hookIt = TRUE;
|
|
|
|
// pass on to PDO
|
|
break;
|
|
|
|
default:
|
|
|
|
UHCD_KdTrap(("Bogus DeviceState = %x\n", DeviceState));
|
|
}
|
|
|
|
if (hookIt) {
|
|
UHCD_KdPrint((2, "'Set PowerIrp Completion Routine\n"));
|
|
IoSetCompletionRoutine(Irp,
|
|
UHCD_PowerIrp_Complete,
|
|
// always pass FDO to completion routine
|
|
DeviceObject,
|
|
hookIt,
|
|
hookIt,
|
|
hookIt);
|
|
}
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
UHCD_PowerIrp_Complete(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when the port driver completes an IRP.
|
|
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the device object for the class device.
|
|
|
|
Irp - Irp completed.
|
|
|
|
Context - Driver defined context.
|
|
|
|
Return Value:
|
|
|
|
The function value is the final status from the operation.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_OBJECT deviceObject;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
NTSTATUS ntStatus;
|
|
|
|
UHCD_KdPrint((2, "' enter UHCD_PowerIrp_Complete\n"));
|
|
|
|
deviceObject = (PDEVICE_OBJECT) Context;
|
|
deviceExtension = (PDEVICE_EXTENSION) deviceObject->DeviceExtension;
|
|
irpStack = IoGetCurrentIrpStackLocation (Irp);
|
|
|
|
UHCD_KdPrint((1, "'Controller is in D0\n"));
|
|
LOGENTRY(LOG_MISC, 'POWc', deviceExtension->CurrentDevicePowerState,
|
|
0, Irp);
|
|
|
|
// This function should only be called whe the controller
|
|
// is put in D0
|
|
UHCD_ASSERT(irpStack->MajorFunction == IRP_MJ_POWER);
|
|
UHCD_ASSERT(irpStack->MinorFunction == IRP_MN_SET_POWER);
|
|
UHCD_ASSERT(irpStack->Parameters.Power.Type==DevicePowerState);
|
|
UHCD_ASSERT(irpStack->Parameters.Power.State.DeviceState==PowerDeviceD0);
|
|
|
|
#ifdef JD
|
|
//TEST_TRAP();
|
|
#endif
|
|
|
|
ntStatus = deviceExtension->LastPowerUpStatus = Irp->IoStatus.Status;
|
|
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
deviceExtension->CurrentDevicePowerState = PowerDeviceD0;
|
|
}
|
|
|
|
UHCD_KdPrint((2, "' exit UHCD_PowerIrp_Complete\n"));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
VOID
|
|
UHCD_Unload(
|
|
IN PDRIVER_OBJECT DriverObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Free all the allocated resources, etc.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - pointer to a driver object
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Free any global resources
|
|
//
|
|
|
|
UHCD_KdPrint((2, "'unloading\n"));
|
|
|
|
#ifdef DEBUG_LOG
|
|
//
|
|
// free our debug trace log
|
|
//
|
|
UHCD_LogFree();
|
|
#endif
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
UHCD_CreateDeviceObject(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN OUT PDEVICE_OBJECT *DeviceObject,
|
|
IN PUNICODE_STRING DeviceNameUnicodeString
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to create a new instance of a USB host
|
|
controller.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - pointer to the driver object for USBD.
|
|
|
|
*DeviceObject - ptr to DeviceObject ptr to be filled
|
|
in with the device object we create.
|
|
|
|
Configuration - ptr to configuration data to be stored
|
|
in the device extension.
|
|
|
|
DeviceNameUnicodeString - optional pointer to a device
|
|
name for this FDO, can be NULL
|
|
|
|
Return Value:
|
|
|
|
NT status code
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS ntStatus;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
|
|
PAGED_CODE();
|
|
|
|
UHCD_KdPrint((2, "'enter UHCD_CreateDeviceObject\n"));
|
|
|
|
ntStatus = IoCreateDevice(DriverObject,
|
|
sizeof (DEVICE_EXTENSION),
|
|
DeviceNameUnicodeString, // Name
|
|
FILE_DEVICE_CONTROLLER,
|
|
0,
|
|
FALSE, //NOT Exclusive
|
|
DeviceObject);
|
|
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION) ((*DeviceObject)->DeviceExtension);
|
|
|
|
|
|
UHCD_KdPrint((2, "'UHCD_CreateDeviceObject: device object %x device extension = %x\n",
|
|
*DeviceObject, deviceExtension));
|
|
|
|
} else if (*DeviceObject) {
|
|
IoDeleteDevice(*DeviceObject);
|
|
}
|
|
|
|
UHCD_KdPrint((2, "'exit UHCD_CreateDeviceObject (%x)\n", ntStatus));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
UHCD_PnPAddDevice(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PDEVICE_OBJECT PhysicalDeviceObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to create a new instance of a USB host controller
|
|
|
|
Arguments:
|
|
|
|
DriverObject - pointer to the driver object for this instance of UHCD
|
|
|
|
PhysicalDeviceObject - pointer to a device object created by the bus
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if successful,
|
|
STATUS_UNSUCCESSFUL otherwise
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS ntStatus;
|
|
PDEVICE_OBJECT deviceObject = NULL;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
UNICODE_STRING deviceNameUnicodeString;
|
|
ULONG deviceNameHandle;
|
|
ULONG disableController = 0;
|
|
|
|
UHCD_KdBreak((2, "'UHCD_PnPAddDevice\n"));
|
|
|
|
LOGENTRY(LOG_MISC, 'ADDd', 0, 0, PhysicalDeviceObject);
|
|
//#ifdef JD
|
|
// TEST_TRAP();
|
|
//#endif
|
|
|
|
//UHCD_GetGlobalRegistryParameters(&disableController);
|
|
|
|
if (disableController) {
|
|
ntStatus = STATUS_UNSUCCESSFUL;
|
|
goto UHCD_PnPAddDevice_Done;
|
|
}
|
|
|
|
|
|
//
|
|
// Let USBD generate a device name
|
|
//
|
|
deviceNameHandle = USBD_AllocateDeviceName(&deviceNameUnicodeString);
|
|
|
|
ntStatus = UHCD_CreateDeviceObject( DriverObject,
|
|
&deviceObject,
|
|
&deviceNameUnicodeString);
|
|
|
|
LOGENTRY(LOG_MISC, 'cdnS', 0, 0, ntStatus);
|
|
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
|
|
deviceExtension = deviceObject->DeviceExtension;
|
|
|
|
RtlZeroMemory(deviceExtension, sizeof(DEVICE_EXTENSION));
|
|
|
|
deviceExtension->PhysicalDeviceObject = PhysicalDeviceObject;
|
|
deviceExtension->DeviceNameHandle = deviceNameHandle;
|
|
|
|
//
|
|
// until we get a start we will comsider ourselves OFF
|
|
//
|
|
deviceExtension->CurrentDevicePowerState = PowerDeviceD3;
|
|
|
|
//deviceExtension->NeedCleanup = FALSE;
|
|
//deviceExtension->BWReclimationEnabled = FALSE;
|
|
//deviceExtension->Stopped = FALSE;
|
|
|
|
deviceExtension->TopOfStackDeviceObject =
|
|
IoAttachDeviceToDeviceStack(deviceObject, PhysicalDeviceObject);
|
|
|
|
//
|
|
// Indicate that the device object is ready for requests.
|
|
//
|
|
|
|
if (deviceExtension->TopOfStackDeviceObject) {
|
|
|
|
//
|
|
// a device object has been created, register with the bus driver now
|
|
//
|
|
|
|
USBD_RegisterHostController(PhysicalDeviceObject,
|
|
deviceObject,
|
|
deviceExtension->TopOfStackDeviceObject,
|
|
DriverObject,
|
|
UHCD_DeferredStartDevice,
|
|
UHCD_SetDevicePowerState,
|
|
UHCD_ExternalGetCurrentFrame,
|
|
#ifndef WIN98
|
|
UHCD_ExternalGetConsumedBW,
|
|
#endif
|
|
UHCD_SubmitFastIsoUrb,
|
|
deviceNameHandle);
|
|
{
|
|
// make sure our USBD and HCD used matching hcdi header files
|
|
PUSBD_EXTENSION usbdExtension;
|
|
usbdExtension = deviceObject->DeviceExtension;
|
|
if (usbdExtension->Length != sizeof(USBD_EXTENSION)) {
|
|
UHCD_KdTrap(("UHCD/USBD version mismatch\n"));
|
|
ntStatus = STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
deviceObject->Flags |= DO_POWER_PAGABLE;
|
|
deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
|
|
|
|
}
|
|
|
|
RtlFreeUnicodeString(&deviceNameUnicodeString);
|
|
|
|
UHCD_PnPAddDevice_Done:
|
|
|
|
LOGENTRY(LOG_MISC, 'addD', 0, 0, ntStatus);
|
|
|
|
UHCD_KdPrint((2, "'exit UHCD_PnPAddDevice (%x)\n", ntStatus));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
UHCD_DeferIrpCompletion(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when the port driver completes an IRP.
|
|
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the device object for the class device.
|
|
|
|
Irp - Irp completed.
|
|
|
|
Context - Driver defined context.
|
|
|
|
Return Value:
|
|
|
|
The function value is the final status from the operation.
|
|
|
|
--*/
|
|
{
|
|
PKEVENT event = Context;
|
|
|
|
|
|
KeSetEvent(event,
|
|
1,
|
|
FALSE);
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
UHCD_ReadWriteConfig(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN BOOLEAN Read,
|
|
IN PVOID Buffer,
|
|
IN ULONG Offset,
|
|
IN ULONG Length
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine reads or write config space.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Physical DeviceObject for this USB controller.
|
|
|
|
Read - TRUE if read, FALSE if write.
|
|
|
|
Buffer - The info to read or write.
|
|
|
|
Offset - The offset in config space to read or write.
|
|
|
|
Length - The length to transfer.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION nextStack;
|
|
PIRP irp;
|
|
NTSTATUS ntStatus;
|
|
KEVENT event;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (Read) {
|
|
memset(Buffer, '\0', Length);
|
|
}
|
|
|
|
irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
|
|
|
|
if (!irp) {
|
|
UHCD_KdTrap(("failed to allocate Irp\n"));
|
|
return;
|
|
}
|
|
|
|
// All PnP IRP's need the Status field initialized to STATUS_NOT_SUPPORTED.
|
|
irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
|
|
IoSetCompletionRoutine(irp,
|
|
UHCD_DeferIrpCompletion,
|
|
&event,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE);
|
|
|
|
|
|
nextStack = IoGetNextIrpStackLocation(irp);
|
|
UHCD_ASSERT(nextStack != NULL);
|
|
nextStack->MajorFunction= IRP_MJ_PNP;
|
|
nextStack->MinorFunction= Read ? IRP_MN_READ_CONFIG : IRP_MN_WRITE_CONFIG;
|
|
nextStack->Parameters.ReadWriteConfig.WhichSpace = 0; /*PCI_WHICHSPACE_CONFIG */
|
|
nextStack->Parameters.ReadWriteConfig.Buffer = Buffer;
|
|
nextStack->Parameters.ReadWriteConfig.Offset = Offset;
|
|
nextStack->Parameters.ReadWriteConfig.Length = Length;
|
|
|
|
ntStatus = IoCallDriver(DeviceObject,
|
|
irp);
|
|
|
|
UHCD_KdPrint((2, "'ntStatus from IoCallDriver to PCI = 0x%x\n", ntStatus));
|
|
|
|
if (ntStatus == STATUS_PENDING) {
|
|
// wait for irp to complete
|
|
|
|
TEST_TRAP(); // first time we hit this
|
|
|
|
KeWaitForSingleObject(
|
|
&event,
|
|
Suspended,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
}
|
|
|
|
if (!NT_SUCCESS(ntStatus)) {
|
|
// failed? this is probably a bug
|
|
UHCD_KdTrap(("ReadWriteConfig failed, why?\n"));
|
|
}
|
|
|
|
IoFreeIrp(irp);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
UHCD_QueryCapabilities(
|
|
IN PDEVICE_OBJECT PdoDeviceObject,
|
|
IN PDEVICE_CAPABILITIES DeviceCapabilities
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine reads or write config space.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Physical DeviceObject for this USB controller.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION nextStack;
|
|
PIRP irp;
|
|
NTSTATUS ntStatus;
|
|
KEVENT event;
|
|
|
|
PAGED_CODE();
|
|
|
|
// init the caps structure before calldown
|
|
RtlZeroMemory(DeviceCapabilities, sizeof(DEVICE_CAPABILITIES));
|
|
DeviceCapabilities->Size = sizeof(DEVICE_CAPABILITIES);
|
|
DeviceCapabilities->Version = 1;
|
|
DeviceCapabilities->Address = -1;
|
|
DeviceCapabilities->UINumber = -1;
|
|
|
|
irp = IoAllocateIrp(PdoDeviceObject->StackSize, FALSE);
|
|
|
|
if (!irp) {
|
|
UHCD_KdTrap(("failed to allocate Irp\n"));
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
// All PnP IRP's need the Status field initialized to STATUS_NOT_SUPPORTED.
|
|
irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
|
|
|
nextStack = IoGetNextIrpStackLocation(irp);
|
|
UHCD_ASSERT(nextStack != NULL);
|
|
nextStack->MajorFunction= IRP_MJ_PNP;
|
|
nextStack->MinorFunction= IRP_MN_QUERY_CAPABILITIES;
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
|
|
IoSetCompletionRoutine(irp,
|
|
UHCD_DeferIrpCompletion,
|
|
&event,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE);
|
|
|
|
nextStack->Parameters.DeviceCapabilities.Capabilities = DeviceCapabilities;
|
|
|
|
ntStatus = IoCallDriver(PdoDeviceObject,
|
|
irp);
|
|
|
|
UHCD_KdPrint((2, "'ntStatus from IoCallDriver to PCI = 0x%x\n", ntStatus));
|
|
|
|
if (ntStatus == STATUS_PENDING) {
|
|
// wait for irp to complete
|
|
|
|
|
|
|
|
KeWaitForSingleObject(
|
|
&event,
|
|
Suspended,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
ntStatus = irp->IoStatus.Status;
|
|
}
|
|
|
|
#if DBG
|
|
if (!NT_SUCCESS(ntStatus)) {
|
|
// failed? this is probably a bug
|
|
UHCD_KdTrap(("QueryCapabilities failed, why?\n"));
|
|
}
|
|
#endif
|
|
|
|
IoFreeIrp(irp);
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
#if 0
|
|
BOOLEAN
|
|
UHCD_StopController(
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Starts the USB host controller executing the schedule.
|
|
Start Controller is called in by KeSynchronizeExecution.
|
|
|
|
Arguments:
|
|
|
|
Context - DeviceData for this USB controller.
|
|
|
|
Return Value:
|
|
|
|
TRUE
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
USHORT cmd;
|
|
|
|
deviceExtension = Context;
|
|
|
|
// no more interrupts
|
|
cmd = 0;
|
|
WRITE_PORT_USHORT(INTERRUPT_MASK_REG(deviceExtension), cmd);
|
|
|
|
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
NTSTATUS
|
|
UHCD_StopDevice(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Resets the USB host controller and disconnects the interrupt.
|
|
if a USB bios is present it is re-started.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - DeviceObject of the controller to stop
|
|
|
|
Return Value:
|
|
|
|
NT status code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
LARGE_INTEGER startTime;
|
|
USHORT cmd;
|
|
USHORT status;
|
|
KIRQL irql;
|
|
|
|
UHCD_KdPrint((2, "'enter UHCD_StopDevice \n"));
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
if (deviceExtension->HcFlags & HCFLAG_HCD_STOPPED) {
|
|
// already stopped, bail
|
|
goto UHCD_StopDevice_Done;
|
|
}
|
|
|
|
UHCD_DisableIdleCheck(deviceExtension);
|
|
|
|
if (!(deviceExtension->HcFlags & HCFLAG_GOT_IO)) {
|
|
// if we don't have io ports we can't stop
|
|
// the controller
|
|
//
|
|
// This check is here because Win98 will send a stop
|
|
// if we fail the start and we mail fail to start because
|
|
// we did not get io ports
|
|
goto UHCD_StopDevice_Done;
|
|
}
|
|
|
|
//
|
|
// disable all interrupts
|
|
//
|
|
|
|
|
|
// KeSynchronizeExecution(deviceExtension->InterruptObject,
|
|
// UHCD_StopController,
|
|
// deviceExtension);
|
|
cmd = 0;
|
|
WRITE_PORT_USHORT(INTERRUPT_MASK_REG(deviceExtension), cmd);
|
|
|
|
//
|
|
// reset the controller
|
|
//
|
|
cmd = UHCD_CMD_RESET;
|
|
WRITE_PORT_USHORT(COMMAND_REG(deviceExtension), cmd);
|
|
|
|
// now wait for HC to reset
|
|
KeQuerySystemTime(&startTime);
|
|
for (;;) {
|
|
LARGE_INTEGER sysTime;
|
|
|
|
cmd = READ_PORT_USHORT(COMMAND_REG(deviceExtension));
|
|
UHCD_KdPrint((2, "'COMMAND = %x\n", cmd));
|
|
if ((cmd & UHCD_CMD_RESET) == 0) {
|
|
break;
|
|
}
|
|
|
|
KeQuerySystemTime(&sysTime);
|
|
if (sysTime.QuadPart - startTime.QuadPart > 10000) {
|
|
// timeout trying to rest
|
|
#if DBG
|
|
UHCD_KdPrint((1, "TIMEOUT RESETTING CONTROLLER! \n"));
|
|
UHCD_KdPrint((1, "'Port Resources @ %x Ports Available \n",
|
|
deviceExtension->Port));
|
|
TRAP();
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// after reset the halt bit is set, clear status register just
|
|
// in case.
|
|
//
|
|
status = 0xff;
|
|
WRITE_PORT_USHORT(STATUS_REG(deviceExtension), status);
|
|
|
|
#if DBG
|
|
{
|
|
cmd = READ_PORT_USHORT(COMMAND_REG(deviceExtension));
|
|
status = READ_PORT_USHORT(STATUS_REG(deviceExtension));
|
|
UHCD_KdBreak((2, "'after reset cmd = %x stat = %x\n", cmd, status));
|
|
}
|
|
#endif
|
|
|
|
UHCD_StopDevice_Done:
|
|
|
|
//
|
|
// handle no more interrupts for the HC
|
|
//
|
|
|
|
KeAcquireSpinLock(&deviceExtension->HcFlagSpin, &irql);
|
|
deviceExtension->HcFlags |= HCFLAG_HCD_STOPPED;
|
|
|
|
if (deviceExtension->InterruptObject) {
|
|
PKINTERRUPT interruptObject;
|
|
|
|
interruptObject = deviceExtension->InterruptObject;
|
|
deviceExtension->InterruptObject = NULL;
|
|
KeReleaseSpinLock(&deviceExtension->HcFlagSpin, irql);
|
|
|
|
IoDisconnectInterrupt(interruptObject);
|
|
} else {
|
|
KeReleaseSpinLock(&deviceExtension->HcFlagSpin, irql);
|
|
}
|
|
|
|
UHCD_KdPrint((2, "'exit UHCD_StopDevice (%x)\n", ntStatus));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
VOID
|
|
UHCD_CleanupDevice(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Cleans up resources allocated for the host controller,
|
|
this routine should undo anything done in START_DEVICE
|
|
that does not require access to the HC hardware.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - DeviceObject of the controller to stop
|
|
|
|
Return Value:
|
|
|
|
NT status code.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PUHCD_PAGE_LIST_ENTRY pageListEntry;
|
|
BOOLEAN waitMore = TRUE;
|
|
LARGE_INTEGER deltaTime;
|
|
#ifdef MAX_DEBUG
|
|
extern LONG UHCD_CommonBufferBytes;
|
|
extern LONG UHCD_TotalAllocatedHeapSpace;
|
|
#endif
|
|
|
|
PAGED_CODE();
|
|
UHCD_KdPrint((2, "'enter UHCD_CleanupDevice \n"));
|
|
|
|
if (deviceExtension->HcFlags & HCFLAG_NEED_CLEANUP) {
|
|
|
|
deviceExtension->HcFlags &= ~HCFLAG_NEED_CLEANUP;
|
|
|
|
if (deviceExtension->RootHubPollTimerInitialized) {
|
|
KeCancelTimer(&deviceExtension->RootHubPollTimer);
|
|
}
|
|
|
|
//
|
|
// If someone issued a reset to one of the root
|
|
// hub ports before the stop there may be timers
|
|
// still outstanding, we wait here fo them to
|
|
// expire.
|
|
//
|
|
|
|
while (deviceExtension->RootHubTimersActive) {
|
|
|
|
//
|
|
// wait 20 ms for our timers to expire
|
|
//
|
|
deltaTime.QuadPart = -10000 * 20;
|
|
(VOID) KeDelayExecutionThread(KernelMode,
|
|
FALSE,
|
|
&deltaTime);
|
|
|
|
waitMore = FALSE;
|
|
}
|
|
|
|
//
|
|
// Wait for the polling timer to expire if we didn't wait
|
|
// for the the root hub timers
|
|
//
|
|
|
|
if (waitMore && deviceExtension->RootHubPollTimerInitialized) {
|
|
deltaTime.QuadPart = -10000 * 10;
|
|
(VOID)KeDelayExecutionThread(KernelMode, FALSE,
|
|
&deltaTime);
|
|
}
|
|
|
|
//
|
|
// free memory allocated for frame list
|
|
//
|
|
|
|
if (deviceExtension->FrameListVirtualAddress != NULL) {
|
|
UHCD_ASSERT(deviceExtension->AdapterObject != NULL);
|
|
|
|
|
|
HalFreeCommonBuffer(deviceExtension->AdapterObject,
|
|
#if DBG
|
|
FRAME_LIST_LENGTH *3,
|
|
#else
|
|
FRAME_LIST_LENGTH *2,
|
|
#endif
|
|
deviceExtension->FrameListLogicalAddress,
|
|
deviceExtension->FrameListVirtualAddress,
|
|
FALSE);
|
|
|
|
|
|
|
|
deviceExtension->FrameListVirtualAddress = NULL;
|
|
|
|
#if DBG
|
|
{
|
|
extern LONG UHCD_CommonBufferBytes;
|
|
UHCD_ASSERT(UHCD_CommonBufferBytes > FRAME_LIST_LENGTH*3);
|
|
UHCD_CommonBufferBytes -= (FRAME_LIST_LENGTH*3);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// free HW descriptors used as interrupt triggers
|
|
if (deviceExtension->PersistantQueueHead) {
|
|
UHCD_FreeHardwareDescriptors(DeviceObject, deviceExtension->PQH_DescriptorList);
|
|
deviceExtension->PersistantQueueHead = NULL;
|
|
}
|
|
|
|
//
|
|
// unmap device registers here
|
|
//
|
|
if (deviceExtension->HcFlags & HCFLAG_UNMAP_REGISTERS) {
|
|
TEST_TRAP();
|
|
}
|
|
|
|
if (deviceExtension->Piix4EP) {
|
|
RETHEAP(deviceExtension->Piix4EP);
|
|
deviceExtension->Piix4EP = NULL;
|
|
}
|
|
|
|
//BUGBUG
|
|
//ASSERT that we have no active endpoints
|
|
//and that no endpoints are on the close list
|
|
|
|
|
|
//
|
|
// free root hub memory
|
|
//
|
|
if (deviceExtension->RootHub) {
|
|
#if DBG
|
|
if (deviceExtension->RootHub->DisabledPort) {
|
|
RETHEAP (deviceExtension->RootHub->DisabledPort);
|
|
}
|
|
#endif
|
|
RETHEAP(deviceExtension->RootHub);
|
|
deviceExtension->RootHub = NULL;
|
|
}
|
|
|
|
//
|
|
// free all pages allocated with HalAllocateCommonBuffer
|
|
//
|
|
|
|
while (!IsListEmpty(&deviceExtension->PageList)) {
|
|
pageListEntry = (PUHCD_PAGE_LIST_ENTRY)
|
|
RemoveHeadList(&deviceExtension->PageList);
|
|
#ifdef MAX_DEBUG
|
|
UHCD_CommonBufferBytes -= pageListEntry->Length;
|
|
#endif
|
|
|
|
HalFreeCommonBuffer(deviceExtension->AdapterObject,
|
|
pageListEntry->Length,
|
|
pageListEntry->LogicalAddress,
|
|
pageListEntry,
|
|
FALSE);
|
|
}
|
|
|
|
//
|
|
// NOTE: may not have an adapter object if getresources fails
|
|
//
|
|
|
|
if (deviceExtension->AdapterObject) {
|
|
KIRQL oldIrql;
|
|
KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
|
|
(deviceExtension->AdapterObject->DmaOperations->PutDmaAdapter)
|
|
(deviceExtension->AdapterObject);
|
|
KeLowerIrql (oldIrql);
|
|
}
|
|
deviceExtension->AdapterObject = NULL;
|
|
#ifdef MAX_DEBUG
|
|
{
|
|
extern LONG UHCD_CommonBufferBytes;
|
|
extern LONG UHCD_TotalAllocatedHeapSpace;
|
|
//
|
|
// Check totalmemory allocated count
|
|
//
|
|
if (UHCD_TotalAllocatedHeapSpace != 0) {
|
|
//
|
|
// memory leak!!
|
|
//
|
|
TRAP();
|
|
}
|
|
|
|
if (UHCD_CommonBufferBytes != 0) {
|
|
//
|
|
// memory leak!!
|
|
//
|
|
TRAP();
|
|
}
|
|
TEST_TRAP();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
UHCD_KdPrint((2, "'exit UHCD_CleanupDevice (%x)\n", STATUS_SUCCESS));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
UHCD_InitializeSchedule(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes the schedule structures for the HCD
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - DeviceObject for this USB controller.
|
|
|
|
Return Value:
|
|
|
|
NT Status code.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
ULONG i, length;
|
|
PHW_QUEUE_HEAD persistantQueueHead;
|
|
PUHCD_HARDWARE_DESCRIPTOR_LIST pqh_DescriptorList;
|
|
|
|
PAGED_CODE();
|
|
UHCD_KdPrint((2, "'enter UHCD_InitializeSchedule\n"));
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// allocate a contiguous range of physical memory for the frame list --
|
|
// we will need enough for 1024 physical addresses
|
|
//
|
|
|
|
length = FRAME_LIST_LENGTH;
|
|
|
|
//
|
|
// Allocate a common buffer for the frame list (that is programmed into
|
|
// the hardware later) as well as a copy of the active frame list.
|
|
//
|
|
|
|
deviceExtension->FrameListVirtualAddress =
|
|
HalAllocateCommonBuffer(deviceExtension->AdapterObject,
|
|
#if DBG
|
|
length*3,
|
|
#else
|
|
length*2,
|
|
#endif
|
|
&deviceExtension->FrameListLogicalAddress,
|
|
FALSE);
|
|
|
|
|
|
|
|
#if DBG
|
|
{
|
|
extern LONG UHCD_CommonBufferBytes;
|
|
UHCD_CommonBufferBytes += length*3;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Set the copy pointer into the common buffer at the end of the
|
|
// master frame list.
|
|
//
|
|
|
|
deviceExtension->FrameListCopyVirtualAddress =
|
|
((PUCHAR) deviceExtension->FrameListVirtualAddress) + length;
|
|
|
|
if (deviceExtension->FrameListVirtualAddress == NULL ||
|
|
deviceExtension->FrameListCopyVirtualAddress == NULL) {
|
|
if (deviceExtension->FrameListVirtualAddress != NULL) {
|
|
|
|
HalFreeCommonBuffer(deviceExtension->AdapterObject,
|
|
#if DBG
|
|
length*3,
|
|
#else
|
|
length*2,
|
|
#endif
|
|
deviceExtension->FrameListLogicalAddress,
|
|
deviceExtension->FrameListVirtualAddress,
|
|
FALSE);
|
|
|
|
deviceExtension->FrameListVirtualAddress = NULL;
|
|
|
|
#if DBG
|
|
{
|
|
extern LONG UHCD_CommonBufferBytes;
|
|
UHCD_CommonBufferBytes -= length*3;
|
|
}
|
|
#endif
|
|
}
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto UHCD_InitializeScheduleExit;
|
|
} else {
|
|
RtlZeroMemory(deviceExtension->FrameListVirtualAddress, length);
|
|
RtlZeroMemory(deviceExtension->FrameListCopyVirtualAddress, length);
|
|
#if DBG
|
|
deviceExtension->IsoList =
|
|
(PULONG)(((PUCHAR) deviceExtension->FrameListCopyVirtualAddress) + length);
|
|
RtlZeroMemory(deviceExtension->IsoList, length);
|
|
UHCD_KdPrint((2, "'(%d) bytes allocated for usb iso list va = %x\n", length,
|
|
deviceExtension->IsoList));
|
|
|
|
#endif
|
|
//
|
|
// this should be 1 page
|
|
//
|
|
UHCD_KdPrint((2, "'(%d) bytes allocated for usb frame list va = %x\n", length,
|
|
deviceExtension->FrameListVirtualAddress));
|
|
|
|
UHCD_KdPrint((2, "'(%d) bytes allocated for usb frame list copy va = %x\n", length,
|
|
deviceExtension->FrameListCopyVirtualAddress));
|
|
|
|
}
|
|
|
|
//
|
|
// Initialize the lists of Memory Descriptors
|
|
// used to allocate TDs and packet buffers
|
|
//
|
|
|
|
UHCD_InitializeCommonBufferPool(DeviceObject,
|
|
&deviceExtension->LargeBufferPool,
|
|
UHCD_LARGE_COMMON_BUFFER_SIZE,
|
|
UHCD_RESERVE_LARGE_BUFFERS);
|
|
|
|
UHCD_InitializeCommonBufferPool(DeviceObject,
|
|
&deviceExtension->MediumBufferPool,
|
|
UHCD_MEDIUM_COMMON_BUFFER_SIZE,
|
|
UHCD_RESERVE_MEDIUM_BUFFERS);
|
|
|
|
UHCD_InitializeCommonBufferPool(DeviceObject,
|
|
&deviceExtension->SmallBufferPool,
|
|
UHCD_SMALL_COMMON_BUFFER_SIZE,
|
|
UHCD_RESERVE_SMALL_BUFFERS);
|
|
|
|
|
|
//
|
|
// Now set up our base queues for active endpoints
|
|
//
|
|
|
|
InitializeListHead(&deviceExtension->EndpointList);
|
|
InitializeListHead(&deviceExtension->EndpointLookAsideList);
|
|
InitializeListHead(&deviceExtension->FastIsoEndpointList);
|
|
InitializeListHead(&deviceExtension->FastIsoTransferList);
|
|
|
|
//
|
|
// Queue for released endpoints
|
|
//
|
|
InitializeListHead(&deviceExtension->ClosedEndpointList);
|
|
|
|
//BUGBUG check for error
|
|
|
|
//
|
|
// TDs are allocated for use as interrupt triggers.
|
|
// the first two are used to detect when the sign bit for
|
|
// the frame counter changes.
|
|
//
|
|
// The rest are used to generate interrupts for cleanup and cancel
|
|
//
|
|
|
|
if (!UHCD_AllocateHardwareDescriptors(DeviceObject,
|
|
&deviceExtension->PQH_DescriptorList,
|
|
MAX_TDS_PER_ENDPOINT)) {
|
|
|
|
|
|
HalFreeCommonBuffer(deviceExtension->AdapterObject,
|
|
#if DBG
|
|
length*3,
|
|
#else
|
|
length*2,
|
|
#endif
|
|
deviceExtension->FrameListLogicalAddress,
|
|
deviceExtension->FrameListVirtualAddress,
|
|
FALSE);
|
|
|
|
|
|
deviceExtension->FrameListVirtualAddress = NULL;
|
|
|
|
|
|
#if DBG
|
|
{
|
|
extern LONG UHCD_CommonBufferBytes;
|
|
UHCD_CommonBufferBytes -= length*3;
|
|
}
|
|
#endif
|
|
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto UHCD_InitializeScheduleExit;
|
|
}
|
|
|
|
pqh_DescriptorList = deviceExtension->PQH_DescriptorList;
|
|
persistantQueueHead =
|
|
deviceExtension->PersistantQueueHead = (PHW_QUEUE_HEAD) pqh_DescriptorList->MemoryDescriptor->VirtualAddress;
|
|
|
|
UHCD_KdPrint((2, "'Control Queue Head va = (%x)\n", deviceExtension->PersistantQueueHead));
|
|
|
|
//
|
|
// This is our base queue head, it goes in every frame
|
|
// but never has transfers associated with it.
|
|
// Control Q heads are always added after this guy,
|
|
// Bulk Q heads are always added in front of this guy (end of queue).
|
|
//
|
|
// initialize Horiz ptr to point to himself with the T-Bit set
|
|
//
|
|
persistantQueueHead->HW_HLink = persistantQueueHead->PhysicalAddress;
|
|
SET_T_BIT(persistantQueueHead->HW_HLink);
|
|
persistantQueueHead->HW_VLink = LIST_END;
|
|
persistantQueueHead->Next =
|
|
persistantQueueHead->Prev = persistantQueueHead;
|
|
|
|
// Put the control 'base' queue in every frame
|
|
for (i=0; i < FRAME_LIST_SIZE; i++)
|
|
*( ((PULONG) (deviceExtension->FrameListVirtualAddress)+i) ) =
|
|
persistantQueueHead->PhysicalAddress;
|
|
|
|
//
|
|
// Initialize an empty interrupt schedule.
|
|
//
|
|
|
|
for (i=0; i < MAX_INTERVAL; i++)
|
|
deviceExtension-> InterruptSchedule[i] =
|
|
persistantQueueHead;
|
|
|
|
|
|
// this is a dummy TD we use to generate an interrupt
|
|
// when the sign bit changes on the frame counter
|
|
deviceExtension->TriggerTDList = (PUHCD_TD_LIST) (pqh_DescriptorList->MemoryDescriptor->VirtualAddress +
|
|
UHCD_HW_DESCRIPTOR_SIZE);
|
|
|
|
deviceExtension->TriggerTDList->TDs[0].Active = 0;
|
|
deviceExtension->TriggerTDList->TDs[0].InterruptOnComplete = 1;
|
|
#ifdef VIA_HC
|
|
// VIA Host Controller requires a valid PID even if the TD is inactive
|
|
deviceExtension->TriggerTDList->TDs[0].PID = USB_IN_PID;
|
|
#endif /* VIA_HC */
|
|
|
|
deviceExtension->TriggerTDList->TDs[1].Active = 0;
|
|
deviceExtension->TriggerTDList->TDs[1].InterruptOnComplete = 1;
|
|
#ifdef VIA_HC
|
|
deviceExtension->TriggerTDList->TDs[1].PID = USB_IN_PID;
|
|
#endif /* VIA_HC */
|
|
deviceExtension->TriggerTDList->TDs[0].HW_Link =
|
|
deviceExtension->TriggerTDList->TDs[1].HW_Link = persistantQueueHead->PhysicalAddress;
|
|
|
|
//
|
|
// The PIIX3 has the following bug:
|
|
//
|
|
// If a frame babble occurrs an interrupt in generated with no way to
|
|
// clear it -- the host controller will continue to generate interrupts
|
|
// until an active TD is encountered.
|
|
//
|
|
// The workaround for this is to put an active TD in the schedule
|
|
// (on the persistent queue head). We activate this TD whenever a TD
|
|
// completes with the babble bit set.
|
|
//
|
|
|
|
|
|
//
|
|
// set up a TD for frame babble recovery
|
|
//
|
|
{
|
|
PHW_TRANSFER_DESCRIPTOR transferDescriptor;
|
|
|
|
transferDescriptor =
|
|
&deviceExtension->TriggerTDList->TDs[2];
|
|
|
|
transferDescriptor->Active = 1;
|
|
transferDescriptor->Isochronous = 1;
|
|
transferDescriptor->InterruptOnComplete = 0;
|
|
transferDescriptor->PID = USB_OUT_PID;
|
|
transferDescriptor->Address = 0;
|
|
transferDescriptor->Endpoint = 1;
|
|
transferDescriptor->ErrorCounter = 0;
|
|
transferDescriptor->PacketBuffer = 0;
|
|
transferDescriptor->MaxLength =
|
|
NULL_PACKET_LENGTH;
|
|
|
|
// point to ourself
|
|
transferDescriptor->HW_Link =
|
|
transferDescriptor->PhysicalAddress;
|
|
//SET_T_BIT(transferDescriptor->HW_Link);
|
|
|
|
// point the persistent queue head at this TD
|
|
persistantQueueHead->HW_VLink =
|
|
transferDescriptor->PhysicalAddress;
|
|
|
|
deviceExtension->FrameBabbleRecoverTD =
|
|
transferDescriptor;
|
|
}
|
|
|
|
// Initilaize the remainder of the trigger TDs for use by the
|
|
// transfer code -- used to generate interrupts.
|
|
for (i=UHCD_FIRST_TRIGGER_TD; i<MAX_TDS_PER_ENDPOINT; i++) {
|
|
PHW_TRANSFER_DESCRIPTOR transferDescriptor;
|
|
|
|
transferDescriptor =
|
|
&deviceExtension->TriggerTDList->TDs[i];
|
|
|
|
transferDescriptor->PID = USB_IN_PID;
|
|
transferDescriptor->Frame = 0;
|
|
}
|
|
|
|
|
|
// Initialize internal frame counters.
|
|
deviceExtension->FrameHighPart =
|
|
deviceExtension->LastFrame = 0;
|
|
|
|
// BUGBUG for now just insert trigger TDs
|
|
*( ((PULONG) (deviceExtension->FrameListVirtualAddress)) ) =
|
|
deviceExtension->TriggerTDList->TDs[0].PhysicalAddress;
|
|
|
|
*( ((PULONG) (deviceExtension->FrameListVirtualAddress) + FRAME_LIST_SIZE-1) ) =
|
|
deviceExtension->TriggerTDList->TDs[1].PhysicalAddress;
|
|
|
|
// schedule has been set up, make copy here
|
|
RtlCopyMemory(deviceExtension->FrameListCopyVirtualAddress,
|
|
deviceExtension->FrameListVirtualAddress,
|
|
FRAME_LIST_SIZE * sizeof(HW_DESCRIPTOR_PHYSICAL_ADDRESS));
|
|
UHCD_KdPrint((2, "'FrameListCopy = %x FrameList= %x\n",
|
|
deviceExtension->FrameListCopyVirtualAddress,
|
|
deviceExtension->FrameListVirtualAddress));
|
|
//
|
|
// Initilaize Root hub variables
|
|
//
|
|
|
|
deviceExtension->RootHubDeviceAddress = USB_DEFAULT_DEVICE_ADDRESS;
|
|
deviceExtension->RootHubInterruptEndpoint = NULL;
|
|
|
|
//
|
|
// Initialize Isoch variables
|
|
//
|
|
|
|
deviceExtension->LastFrameProcessed = 0;
|
|
|
|
//
|
|
// Initialize Misc variables
|
|
//
|
|
|
|
deviceExtension->EndpointListBusy = FALSE;
|
|
deviceExtension->LastPowerUpStatus = STATUS_SUCCESS;
|
|
|
|
UHCD_InitBandwidthTable(DeviceObject);
|
|
|
|
KeInitializeSpinLock(&deviceExtension->EndpointListSpin);
|
|
|
|
KeInitializeSpinLock(&deviceExtension->HcFlagSpin);
|
|
KeInitializeSpinLock(&deviceExtension->HcDmaSpin);
|
|
KeInitializeSpinLock(&deviceExtension->HcScheduleSpin);
|
|
deviceExtension->HcDma = -1;
|
|
|
|
//
|
|
// fix PIIX4 issues.
|
|
//
|
|
UHCD_FixPIIX4(DeviceObject);
|
|
|
|
UHCD_InitializeScheduleExit:
|
|
|
|
LOGENTRY(LOG_MISC, 'BASE', deviceExtension->FrameListVirtualAddress, 0, DeviceObject);
|
|
|
|
UHCD_KdPrint((2, "'exit UHCD_InitializeSchedule (%x)\n", ntStatus));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
UHCD_StartGlobalReset(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes the hardware registers in the host controller.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - DeviceObject for this USB controller.
|
|
|
|
Return Value:
|
|
|
|
NT Status code.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
|
|
PAGED_CODE();
|
|
UHCD_KdPrint((2, "'enter UHCD_StartGlobalReset\n"));
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
LOGENTRY(LOG_MISC, 'InHW', DeviceObject, deviceExtension, 0);
|
|
|
|
UHCD_KdPrint((2, "'before init -- hardware state: Command = %x Status = %x interrupt mask %x\nFrame Base = %x\n",
|
|
READ_PORT_USHORT(COMMAND_REG(deviceExtension)),
|
|
READ_PORT_USHORT(STATUS_REG(deviceExtension)),
|
|
READ_PORT_USHORT(INTERRUPT_MASK_REG(deviceExtension)),
|
|
READ_PORT_ULONG(FRAME_LIST_BASE_REG(deviceExtension))));
|
|
|
|
//
|
|
// Perform global reset on the controller
|
|
//
|
|
// A global reset of the USB requires 20ms, we
|
|
// begin the process here and finish later (in the
|
|
// hub emulation code)
|
|
|
|
UHCD_KdPrint((2, "'Begin Global Reset of Host Controller \n"));
|
|
|
|
WRITE_PORT_USHORT(COMMAND_REG(deviceExtension), UHCD_CMD_GLOBAL_RESET);
|
|
|
|
UHCD_KdPrint((2, "'exit UHCD_StartGlobalReset -- (STATUS_SUCCESS)\n"));
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
UHCD_CompleteGlobalReset(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Starts the USB host controller executing the schedule,
|
|
his routine is called when the global reset for the
|
|
controller has completed.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - DeviceObject for this USB controller.
|
|
|
|
Return Value:
|
|
|
|
NT Status code.
|
|
|
|
--*/
|
|
{
|
|
PHYSICAL_ADDRESS frameListBaseAddress;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
USHORT cmd;
|
|
|
|
UHCD_KdPrint((2, "'enter CompleteGlobalReset\n"));
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// Initialization has completed
|
|
//
|
|
|
|
UHCD_KdPrint((2, "'Initialization Completed, starting controller\n"));
|
|
|
|
// clear the global reset bit
|
|
cmd = READ_PORT_USHORT(COMMAND_REG(deviceExtension));
|
|
WRITE_PORT_USHORT(COMMAND_REG(deviceExtension), cmd &= ~UHCD_CMD_GLOBAL_RESET);
|
|
|
|
UHCD_KdPrint((2, "'after global reset -- hardware state: Command = %x Status = %x interrupt mask %x\nFrame Base = %x\n",
|
|
READ_PORT_USHORT(COMMAND_REG(deviceExtension)),
|
|
READ_PORT_USHORT(STATUS_REG(deviceExtension)),
|
|
READ_PORT_USHORT(INTERRUPT_MASK_REG(deviceExtension)),
|
|
READ_PORT_ULONG(FRAME_LIST_BASE_REG(deviceExtension))));
|
|
|
|
//
|
|
// do a HC reset
|
|
//
|
|
|
|
// cmd = READ_PORT_USHORT(COMMAND_REG(deviceExtension)) | UHCD_CMD_RESET;
|
|
// WRITE_PORT_USHORT(COMMAND_REG(deviceExtension), cmd);
|
|
|
|
// stall for 10 microseconds
|
|
// KeStallExecutionProcessor(10); //stall for 10 microseconds
|
|
|
|
// cmd = READ_PORT_USHORT(COMMAND_REG(deviceExtension));
|
|
|
|
//
|
|
// HCReset bit should be cleared when HC completes the reset
|
|
//
|
|
|
|
// if (cmd & UHCD_CMD_RESET) {
|
|
// UHCD_KdPrint((2, "'Host Controller unable to reset!!!\n"));
|
|
// TRAP();
|
|
// }
|
|
|
|
//
|
|
// program the frame list base address
|
|
//
|
|
|
|
frameListBaseAddress = deviceExtension->FrameListLogicalAddress;
|
|
|
|
WRITE_PORT_ULONG(FRAME_LIST_BASE_REG(deviceExtension), frameListBaseAddress.LowPart);
|
|
UHCD_KdPrint((2, "'Frame list base address programmed to (physical) %x.\n", frameListBaseAddress));
|
|
|
|
UHCD_KdPrint((2, "'after init -- hardware state: Command = %x Status = %x interrupt mask %x\nFrame Base = %x\n",
|
|
READ_PORT_USHORT(COMMAND_REG(deviceExtension)),
|
|
READ_PORT_USHORT(STATUS_REG(deviceExtension)),
|
|
READ_PORT_USHORT(INTERRUPT_MASK_REG(deviceExtension)),
|
|
READ_PORT_ULONG(FRAME_LIST_BASE_REG(deviceExtension))));
|
|
|
|
if (deviceExtension->SteppingVersion >= UHCD_B0_STEP) {
|
|
#ifdef ENABLE_B0_FEATURES
|
|
//
|
|
// set maxpacket for bandwidth reclimation
|
|
//
|
|
|
|
// TEST_TRAP();
|
|
WRITE_PORT_USHORT(COMMAND_REG(deviceExtension), UHCD_CMD_MAXPKT_64);
|
|
UHCD_KdPrint((2, "'Set MaxPacket to 64\n"));
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// Enable Interrupts on the controller
|
|
//
|
|
|
|
|
|
UHCD_KdPrint((2, "'Enable Interrupts \n"));
|
|
cmd = UHCD_INT_MASK_IOC | UHCD_INT_MASK_TIMEOUT | UHCD_INT_MASK_RESUME;
|
|
if (deviceExtension->SteppingVersion >= UHCD_B0_STEP) {
|
|
// enable short packet detect
|
|
cmd |= UHCD_INT_MASK_SHORT;
|
|
}
|
|
|
|
WRITE_PORT_USHORT(INTERRUPT_MASK_REG(deviceExtension), cmd);
|
|
|
|
// set the SOF modify to whatever we found before
|
|
// the reset
|
|
|
|
UHCD_KdPrint((1, "'Setting SOF Modify to %d\n",
|
|
deviceExtension->SavedSofModify));
|
|
|
|
WRITE_PORT_UCHAR(SOF_MODIFY_REG(deviceExtension),
|
|
deviceExtension->SavedSofModify);
|
|
|
|
//
|
|
// Start the controller schedule
|
|
//
|
|
|
|
UHCD_KdPrint((2, "'Set Run/Stop bit \n"));
|
|
|
|
cmd = READ_PORT_USHORT(COMMAND_REG(deviceExtension)) | UHCD_CMD_RUN;
|
|
WRITE_PORT_USHORT(COMMAND_REG(deviceExtension), cmd);
|
|
|
|
//
|
|
// Make sure that the controller is really running and if not, kick it
|
|
//
|
|
|
|
UhcdKickStartController(DeviceObject);
|
|
|
|
UHCD_KdPrint((2, "'after start -- hardware state: Command = %x Status = %x interrupt mask %x\nFrame Base = %x\n",
|
|
READ_PORT_USHORT(COMMAND_REG(deviceExtension)),
|
|
READ_PORT_USHORT(STATUS_REG(deviceExtension)),
|
|
READ_PORT_USHORT(INTERRUPT_MASK_REG(deviceExtension)),
|
|
READ_PORT_ULONG(FRAME_LIST_BASE_REG(deviceExtension))));
|
|
|
|
|
|
UHCD_KdPrint((2, "'exit CompleteGlobalReset\n"));
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
ULONG
|
|
UHCD_GetCurrentFrame(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
returns the current frame number as an unsigned 32-bit value.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to a device object
|
|
|
|
Return Value:
|
|
|
|
Current Frame Number.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
ULONG currentFrame, highPart, frameNumber;
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// The following algorithm compliments
|
|
// of jfuller and kenr
|
|
//
|
|
|
|
// get Hcd's high part of frame number
|
|
highPart = deviceExtension->FrameHighPart;
|
|
|
|
// get 11-bit frame number, high 17-bits are 0
|
|
frameNumber = (ULONG) READ_PORT_USHORT(FRAME_LIST_CURRENT_INDEX_REG(deviceExtension));
|
|
|
|
currentFrame = ((frameNumber & 0x0bff) | highPart) +
|
|
((frameNumber ^ highPart) & 0x0400);
|
|
|
|
//UHCD_KdPrint((2, "'exit UHCD_GetCurrentFrame = %d\n", currentFrame));
|
|
|
|
return currentFrame;
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
UHCD_SaveHCstate(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - DeviceObject for this USB controller.
|
|
|
|
Return Value:
|
|
|
|
NT status code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
USHORT cmd;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
|
|
UHCD_KdPrint((1, "'saving host controller state\n"));
|
|
LOGENTRY(LOG_MISC, 'HCsv', 0, 0, 0);
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
//
|
|
// save some state info
|
|
//
|
|
deviceExtension->SavedInterruptEnable =
|
|
READ_PORT_USHORT(INTERRUPT_MASK_REG(deviceExtension));
|
|
|
|
cmd = READ_PORT_USHORT(COMMAND_REG(deviceExtension));
|
|
// save the cmd regs in the "stopped" state
|
|
cmd &= ~UHCD_CMD_RUN;
|
|
|
|
deviceExtension->SavedCommandReg = cmd;
|
|
|
|
// additonal saved info needed to restore from hibernate
|
|
deviceExtension->SavedFRNUM =
|
|
READ_PORT_USHORT(FRAME_LIST_CURRENT_INDEX_REG(deviceExtension));
|
|
deviceExtension->SavedFRBASEADD =
|
|
READ_PORT_ULONG(FRAME_LIST_BASE_REG(deviceExtension));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
UHCD_RestoreHCstate(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - DeviceObject for this USB controller.
|
|
|
|
Return Value:
|
|
|
|
NT status code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
BOOLEAN lostPower = FALSE;
|
|
LARGE_INTEGER deltaTime;
|
|
BOOLEAN apm = FALSE;
|
|
|
|
UHCD_KdPrint((1, "'restoring host controller state\n"));
|
|
LOGENTRY(LOG_MISC, 'HCrs', 0, 0, 0);
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// this check tells us that power was turned off
|
|
// to the controller.
|
|
//
|
|
// IBM Aptiva rapid resume will turn off power to
|
|
// the controller without going thru the OS
|
|
// may APM BIOSes do this too
|
|
//
|
|
|
|
// see if we were in D3, if so restore additional info
|
|
// necessary to recover from hibernate
|
|
|
|
if (deviceExtension->HcFlags & HCFLAG_LOST_POWER) {
|
|
UHCD_KdPrint((0, "'restoring HC from hibernate\n"));
|
|
|
|
deviceExtension->HcFlags &= ~HCFLAG_LOST_POWER;
|
|
// we will need to do much the same thing we do
|
|
// in START_DEVICE
|
|
|
|
// first restore the HC regs for the schedule
|
|
WRITE_PORT_USHORT(FRAME_LIST_CURRENT_INDEX_REG(deviceExtension),
|
|
deviceExtension->SavedFRNUM);
|
|
WRITE_PORT_ULONG(FRAME_LIST_BASE_REG(deviceExtension),
|
|
deviceExtension->SavedFRBASEADD);
|
|
|
|
// now do a global reset
|
|
ntStatus = UHCD_StartGlobalReset(DeviceObject);
|
|
|
|
if (!NT_SUCCESS(ntStatus)) {
|
|
goto UHCD_RestoreHCstate_Done;
|
|
}
|
|
//
|
|
// Everything is set, we need to wait for the
|
|
// global reset of the Host controller to complete.
|
|
//
|
|
|
|
// 20 ms to reset...
|
|
deltaTime.QuadPart = -10000 * 20;
|
|
|
|
//
|
|
// block here until reset is complete
|
|
//
|
|
|
|
(VOID) KeDelayExecutionThread(KernelMode,
|
|
FALSE,
|
|
&deltaTime);
|
|
|
|
ntStatus = UHCD_CompleteGlobalReset(DeviceObject);
|
|
|
|
lostPower = TRUE;
|
|
|
|
} else {
|
|
//
|
|
// interrupt masks disabled indicates power was turned
|
|
// off to the piix3/piix4.
|
|
//
|
|
lostPower =
|
|
(BOOLEAN) (READ_PORT_USHORT(INTERRUPT_MASK_REG(deviceExtension)) == 0);
|
|
// we get here on APM/ACPI systems
|
|
// note that lostPower should be false on an ACPI system
|
|
apm = TRUE;
|
|
}
|
|
|
|
if (lostPower) {
|
|
// we get here for hibernate, APM suspend or ACPI D2/D3
|
|
// on hibernate we need to re-init the controller
|
|
// on APM we let the BIOS do it.
|
|
|
|
//for APM:
|
|
// lostPower = TRUE if APM BIOS turned off hc
|
|
// apm = TRUE
|
|
|
|
//for ACPI D3/Hibernate
|
|
// HCFLAG_LOST_POWER flag is set
|
|
// lostPower TRUE
|
|
|
|
//for ACPI D2 & APM supprted USB suspend
|
|
// lostPower = FALSE
|
|
// HCFLAG_LOST_POWER is clear
|
|
// apm = FALSE;
|
|
|
|
UHCD_KdPrint((0, "'detected (APM/HIBERNATE) loss of power during suspend\n"));
|
|
|
|
if (apm) {
|
|
//
|
|
// some APM BIOSes trash these registers so we'll have to put
|
|
// them back
|
|
//
|
|
|
|
WRITE_PORT_USHORT(FRAME_LIST_CURRENT_INDEX_REG(deviceExtension),
|
|
deviceExtension->SavedFRNUM);
|
|
|
|
WRITE_PORT_ULONG(FRAME_LIST_BASE_REG(deviceExtension),
|
|
deviceExtension->SavedFRBASEADD);
|
|
}
|
|
|
|
WRITE_PORT_USHORT(INTERRUPT_MASK_REG(deviceExtension),
|
|
deviceExtension->SavedInterruptEnable);
|
|
|
|
WRITE_PORT_USHORT(COMMAND_REG(deviceExtension),
|
|
deviceExtension->SavedCommandReg);
|
|
}
|
|
|
|
UHCD_RestoreHCstate_Done:
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
UHCD_Suspend(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN BOOLEAN SuspendBus
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine suspends the host controller.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - DeviceObject for this USB controller.
|
|
|
|
Return Value:
|
|
|
|
NT status code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
USHORT cmd, status;
|
|
LARGE_INTEGER finishTime, currentTime;
|
|
|
|
PAGED_CODE();
|
|
|
|
UHCD_KdPrint((1, "'suspending Host Controller (Root Hub)\n"));
|
|
LOGENTRY(LOG_MISC, 'RHsu', 0, 0, 0);
|
|
#ifdef JD
|
|
//TEST_TRAP();
|
|
#endif
|
|
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
//may not be in D0 if we haven't started yet.
|
|
//UHCD_ASSERT(deviceExtension->CurrentDevicePowerState == PowerDeviceD0);
|
|
|
|
UHCD_DisableIdleCheck(deviceExtension);
|
|
|
|
#if DBG
|
|
UHCD_KdPrint((2, "'HC regs before suspend\n"));
|
|
UHCD_KdPrint((2, "'cmd register = %x\n",
|
|
READ_PORT_USHORT(COMMAND_REG(deviceExtension)) ));
|
|
UHCD_KdPrint((2, "'status register = %x\n",
|
|
READ_PORT_USHORT(STATUS_REG(deviceExtension)) ));
|
|
UHCD_KdPrint((2, "'interrupt enable register = %x\n",
|
|
READ_PORT_USHORT(INTERRUPT_MASK_REG(deviceExtension)) ));
|
|
|
|
UHCD_KdPrint((2, "'frame list base = %x\n",
|
|
READ_PORT_ULONG(FRAME_LIST_BASE_REG(deviceExtension)) ));
|
|
#endif
|
|
|
|
//
|
|
// save some state info
|
|
//
|
|
deviceExtension->SavedInterruptEnable =
|
|
READ_PORT_USHORT(INTERRUPT_MASK_REG(deviceExtension));
|
|
|
|
// set the run/stop bit
|
|
|
|
cmd = READ_PORT_USHORT(COMMAND_REG(deviceExtension));
|
|
cmd &= ~UHCD_CMD_RUN;
|
|
|
|
deviceExtension->SavedCommandReg = cmd;
|
|
|
|
UHCD_KdPrint((2, "'run/stop = %x\n", cmd));
|
|
WRITE_PORT_USHORT(COMMAND_REG(deviceExtension), cmd);
|
|
|
|
|
|
|
|
KeQuerySystemTime(&finishTime); // get current time
|
|
finishTime.QuadPart += 5000000; // figure when we quit (.5 seconds
|
|
// later)
|
|
|
|
// poll the status reg for the halt bit
|
|
for (;;) {
|
|
status = READ_PORT_USHORT(STATUS_REG(deviceExtension));
|
|
UHCD_KdPrint((2, "'STATUS = %x\n", status));
|
|
if (status & UHCD_STATUS_HCHALT) {
|
|
break;
|
|
}
|
|
KeQuerySystemTime(¤tTime);
|
|
if (currentTime.QuadPart >= finishTime.QuadPart) {
|
|
UHCD_KdPrint((0, "'Warning: Host contoller did not respond to halt req\n"));
|
|
#if DBG
|
|
if (SuspendBus) {
|
|
// this is very bad if we are suspending
|
|
TRAP();
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
|
|
UHCD_KdPrint((2, "'UHCD Suspend RH, schedule stopped\n"));
|
|
|
|
// reset the frame list current index
|
|
WRITE_PORT_USHORT(FRAME_LIST_CURRENT_INDEX_REG(deviceExtension), 0);
|
|
|
|
// re-initialize internal frame counters.
|
|
deviceExtension->FrameHighPart =
|
|
deviceExtension->LastFrame = 0;
|
|
|
|
deviceExtension->XferIdleTime =
|
|
deviceExtension->IdleTime = 0;
|
|
|
|
// clear idle flag sinec we will be running on resume
|
|
// note we leave the state of the rollover ints since
|
|
// it reflects the status of the TDs not the HC schecdule
|
|
|
|
deviceExtension->HcFlags &= ~HCFLAG_IDLE;
|
|
|
|
//
|
|
// we let the hub driver handle suspending the ports
|
|
//
|
|
|
|
// BUGBUG
|
|
// not sure if we need to force resume
|
|
if (SuspendBus) {
|
|
LOGENTRY(LOG_MISC, 'RHew', 0, 0, 0);
|
|
cmd = READ_PORT_USHORT(COMMAND_REG(deviceExtension));
|
|
// FGR=0
|
|
cmd &= ~UHCD_CMD_FORCE_RESUME;
|
|
// EGSM=1
|
|
cmd |= UHCD_CMD_SUSPEND;
|
|
UHCD_KdPrint((2, "'enter suspend = %x\n", cmd));
|
|
WRITE_PORT_USHORT(COMMAND_REG(deviceExtension), cmd);
|
|
}
|
|
|
|
UHCD_KdPrint((2, "'exit UHCD_SuspendHC 0x%x\n", ntStatus));
|
|
#ifdef MAX_DEBUG
|
|
TEST_TRAP();
|
|
#endif
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
UHCD_Resume(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN BOOLEAN DoResumeSignaling
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine resumes the host controller from either the
|
|
OFF or suspended state.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - DeviceObject for this USB controller.
|
|
|
|
Return Value:
|
|
|
|
NT status code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
USHORT cmd;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
LARGE_INTEGER deltaTime;
|
|
|
|
UHCD_KdPrint((1, "'resuming Host Controller (Root Hub)\n"));
|
|
LOGENTRY(LOG_MISC, 'RHre', 0, 0, 0);
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
#if DBG
|
|
UHCD_KdPrint((2, "'HC regs after suspend\n"));
|
|
UHCD_KdPrint((2, "'cmd register = %x\n",
|
|
READ_PORT_USHORT(COMMAND_REG(deviceExtension)) ));
|
|
UHCD_KdPrint((2, "'status register = %x\n",
|
|
READ_PORT_USHORT(STATUS_REG(deviceExtension)) ));
|
|
UHCD_KdPrint((2, "'interrupt enable register = %x\n",
|
|
READ_PORT_USHORT(INTERRUPT_MASK_REG(deviceExtension)) ));
|
|
|
|
UHCD_KdPrint((2, "'frame list base = %x\n",
|
|
READ_PORT_ULONG(FRAME_LIST_BASE_REG(deviceExtension)) ));
|
|
#endif
|
|
|
|
// if we are resuming the controller should be in D0
|
|
UHCD_ASSERT(deviceExtension->CurrentDevicePowerState == PowerDeviceD0);
|
|
|
|
cmd = READ_PORT_USHORT(COMMAND_REG(deviceExtension));
|
|
if (DoResumeSignaling) {
|
|
// force a global resume
|
|
UHCD_KdPrint((2, "'forcing resume = %x\n", cmd));
|
|
cmd |= UHCD_CMD_FORCE_RESUME;
|
|
WRITE_PORT_USHORT(COMMAND_REG(deviceExtension), cmd);
|
|
|
|
//
|
|
// wait 20 ms for our timers to expire
|
|
//
|
|
deltaTime.QuadPart = -10000 * 20;
|
|
(VOID) KeDelayExecutionThread(KernelMode,
|
|
FALSE,
|
|
&deltaTime);
|
|
|
|
//done with resume
|
|
cmd = READ_PORT_USHORT(COMMAND_REG(deviceExtension));
|
|
cmd &= ~(UHCD_CMD_SUSPEND | UHCD_CMD_FORCE_RESUME);
|
|
UHCD_KdPrint((2, "'clear suspend = %x\n", cmd));
|
|
WRITE_PORT_USHORT(COMMAND_REG(deviceExtension), cmd);
|
|
|
|
//
|
|
// start schedule
|
|
//
|
|
|
|
// wait for FGR bit to go low
|
|
do {
|
|
cmd = READ_PORT_USHORT(COMMAND_REG(deviceExtension));
|
|
} while (cmd & UHCD_CMD_FORCE_RESUME);
|
|
|
|
}
|
|
|
|
// start controller
|
|
cmd |= UHCD_CMD_RUN;
|
|
|
|
UHCD_KdPrint((2, "'exit resume = %x\n", cmd));
|
|
WRITE_PORT_USHORT(COMMAND_REG(deviceExtension), cmd);
|
|
|
|
//
|
|
// Note: we let the hub driver handle resume
|
|
// signaling
|
|
//
|
|
|
|
cmd = READ_PORT_USHORT(COMMAND_REG(deviceExtension));
|
|
|
|
|
|
//
|
|
// Make sure that the controller is really running and if not, kick it
|
|
//
|
|
|
|
UhcdKickStartController(DeviceObject);
|
|
|
|
UHCD_KdPrint((2, "'exit UHCD_Resume 0x%x\n", ntStatus));
|
|
#ifdef MAX_DEBUG
|
|
TEST_TRAP();
|
|
#endif
|
|
|
|
// enable the idle check routine
|
|
deviceExtension->HcFlags &= ~HCFLAG_DISABLE_IDLE;
|
|
|
|
UHCD_KdPrint((1, "'Host controller root hub entered D0\n"));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
UHCD_ExternalGetCurrentFrame(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PULONG CurrentFrame
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - DeviceObject for this USB controller.
|
|
|
|
Return Value:
|
|
|
|
NT status code.
|
|
|
|
--*/
|
|
|
|
{
|
|
*CurrentFrame = UHCD_GetCurrentFrame(DeviceObject);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
ULONG
|
|
UHCD_ExternalGetConsumedBW(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - DeviceObject for this USB controller.
|
|
|
|
Return Value:
|
|
|
|
NT status code.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
ULONG low, i;
|
|
|
|
LOGENTRY(LOG_MISC, 'AVbw', 0, 0, 0);
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
low = UHCD_TOTAL_USB_BW;
|
|
|
|
// find the lowest value in our available bandwidth table
|
|
|
|
for (i=0; i<MAX_INTERVAL; i++) {
|
|
//
|
|
// max bytes per frame - bw reaerved for bulk and control
|
|
//
|
|
if (deviceExtension->BwTable[i] < low) {
|
|
low = deviceExtension->BwTable[i];
|
|
}
|
|
}
|
|
|
|
// lowest available - total = consumed bw
|
|
|
|
return UHCD_TOTAL_USB_BW-low;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
UHCD_RootHubPower(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function handles power messages to the root hub, note that
|
|
we save the state of the HC here instead of when the HC itself is
|
|
powered down. The reason for this is for compatibility with APM
|
|
systems that cut power to the HC during a suspend. On these
|
|
systems WDM never sends a power state change mesage to the HC ie
|
|
the HC is assumed to always stay on.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - DeviceObject for this USB controller.
|
|
|
|
Return Value:
|
|
|
|
NT status code.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpStack;
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
|
|
UHCD_KdPrint((2, "'UHCD_RootHubPower\n"));
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
irpStack = IoGetCurrentIrpStackLocation (Irp);
|
|
LOGENTRY(LOG_MISC, 'HCrp', irpStack->MinorFunction, 0, 0);
|
|
|
|
switch (irpStack->MinorFunction) {
|
|
case IRP_MN_WAIT_WAKE:
|
|
UHCD_KdPrint((2, "'IRP_MN_WAIT_WAKE\n"));
|
|
//
|
|
// someone is enabling us for wakeup
|
|
//
|
|
LOGENTRY(LOG_MISC, 'rpWW', 0, 0, 0);
|
|
|
|
TEST_TRAP(); // never seen this before?
|
|
break;
|
|
|
|
case IRP_MN_SET_POWER:
|
|
|
|
switch (irpStack->Parameters.Power.Type) {
|
|
case SystemPowerState:
|
|
LOGENTRY(LOG_MISC, 'SPsp', 0, 0, 0);
|
|
|
|
// should not get here
|
|
UHCD_KdTrap(("RootHubPower -- SystemState\n"));
|
|
break;
|
|
|
|
case DevicePowerState:
|
|
|
|
LOGENTRY(LOG_MISC, 'SPdp', 0, 0, 0);
|
|
switch(irpStack->Parameters.Power.State.DeviceState) {
|
|
case PowerDeviceD0:
|
|
{
|
|
BOOLEAN doResumeSignaling;
|
|
|
|
if (!NT_SUCCESS(deviceExtension->LastPowerUpStatus)) {
|
|
ntStatus = deviceExtension->LastPowerUpStatus;
|
|
} else {
|
|
doResumeSignaling = !(deviceExtension->HcFlags
|
|
& HCFLAG_RH_OFF);
|
|
deviceExtension->HcFlags &= ~HCFLAG_RH_OFF;
|
|
UHCD_SetControllerD0(DeviceObject);
|
|
UHCD_RestoreHCstate(DeviceObject);
|
|
|
|
ntStatus = UHCD_Resume(DeviceObject, doResumeSignaling);
|
|
}
|
|
}
|
|
break;
|
|
case PowerDeviceD1:
|
|
case PowerDeviceD2:
|
|
UHCD_SaveHCstate(DeviceObject);
|
|
ntStatus = UHCD_Suspend(DeviceObject, TRUE);
|
|
break;
|
|
case PowerDeviceD3:
|
|
deviceExtension->HcFlags |= HCFLAG_RH_OFF;
|
|
UHCD_SaveHCstate(DeviceObject);
|
|
ntStatus = UHCD_Suspend(DeviceObject, FALSE);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
VOID
|
|
UhcdKickStartController(IN PDEVICE_OBJECT PDevObj)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Best effort at fixing a hung UHCI device on power up.
|
|
Symptom is that everything is fine and chip in run state,
|
|
but frame counter never increments. It was found that if
|
|
we strobe the run/stop (RS) bit, the frame counter starts incrementing
|
|
and the device starts working.
|
|
|
|
We don't know the exact cause or why the fix appears to work, but the
|
|
addition of this code was requested by MS management to help alleviate the
|
|
problem.
|
|
|
|
Arguments:
|
|
|
|
PDevObj - DeviceObject for this USB controller.
|
|
|
|
Return Value:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)PDevObj->DeviceExtension;
|
|
USHORT cmd;
|
|
USHORT counter;
|
|
ULONG i;
|
|
LARGE_INTEGER deltaTime;
|
|
|
|
|
|
|
|
for (i = 0; i < UHCD_MAX_KICK_STARTS; i++) {
|
|
//
|
|
// Wait at least two frames (2 ms)
|
|
//
|
|
|
|
|
|
deltaTime.QuadPart = -10000 * 2;
|
|
KeDelayExecutionThread(KernelMode, FALSE, &deltaTime);
|
|
|
|
counter = READ_PORT_USHORT(FRAME_LIST_CURRENT_INDEX_REG(pDevExt));
|
|
|
|
if (counter != 0x0000) {
|
|
//
|
|
// It is working splendidly
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// The frame counter is jammed. Kick the chip.
|
|
//
|
|
|
|
cmd = READ_PORT_USHORT(COMMAND_REG(pDevExt)) & ~UHCD_CMD_RUN;
|
|
WRITE_PORT_USHORT(COMMAND_REG(pDevExt), cmd);
|
|
|
|
//
|
|
// Delay two frames (2ms)
|
|
//
|
|
|
|
deltaTime.QuadPart = -10000 * 2;
|
|
KeDelayExecutionThread(KernelMode, FALSE, &deltaTime);
|
|
|
|
cmd |= UHCD_CMD_RUN;
|
|
|
|
WRITE_PORT_USHORT(COMMAND_REG(pDevExt), cmd);
|
|
}
|
|
|
|
LOGENTRY(LOG_MISC | LOG_IO, 'HCks', i, UHCD_MAX_KICK_STARTS, 0);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
UHCD_FixPIIX4(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
PIIX4 hack
|
|
|
|
we will need a dummy bulk queue head inserted in the schedule
|
|
This routine resumes the host controller.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - DeviceObject for this USB controller.
|
|
|
|
Return Value:
|
|
|
|
NT status code.
|
|
|
|
--*/
|
|
{
|
|
PUHCD_ENDPOINT endpoint;
|
|
PHW_TRANSFER_DESCRIPTOR transferDescriptor,
|
|
qhtransferDescriptor;
|
|
PHW_QUEUE_HEAD queueHead;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// create the endpoint
|
|
//
|
|
|
|
endpoint = GETHEAP(NonPagedPool, sizeof(UHCD_ENDPOINT));
|
|
|
|
if (endpoint) {
|
|
deviceExtension->Piix4EP = endpoint;
|
|
endpoint->Type = USB_ENDPOINT_TYPE_BULK;
|
|
endpoint->Sig = SIG_EP;
|
|
endpoint->EndpointFlags = 0;
|
|
|
|
// we will use two of the trigger TDs
|
|
//
|
|
// to set up the dummy qh with TD attached.
|
|
//
|
|
|
|
transferDescriptor =
|
|
&deviceExtension->TriggerTDList->TDs[3];
|
|
|
|
transferDescriptor->Active = 0;
|
|
transferDescriptor->Isochronous = 1;
|
|
transferDescriptor->InterruptOnComplete = 0;
|
|
transferDescriptor->PID = USB_OUT_PID;
|
|
transferDescriptor->Address = 0;
|
|
transferDescriptor->Endpoint = 1;
|
|
transferDescriptor->ErrorCounter = 0;
|
|
transferDescriptor->PacketBuffer = 0;
|
|
transferDescriptor->MaxLength =
|
|
NULL_PACKET_LENGTH;
|
|
|
|
|
|
// point to ourself
|
|
transferDescriptor->HW_Link =
|
|
transferDescriptor->PhysicalAddress;
|
|
|
|
queueHead = (PHW_QUEUE_HEAD)
|
|
&deviceExtension->TriggerTDList->TDs[4];
|
|
|
|
qhtransferDescriptor =
|
|
&deviceExtension->TriggerTDList->TDs[4];
|
|
|
|
UHCD_InitializeHardwareQueueHeadDescriptor(
|
|
DeviceObject,
|
|
queueHead,
|
|
qhtransferDescriptor->PhysicalAddress);
|
|
|
|
UHCD_KdPrint((2, "'PIIX4 dummy endpoint, qh 0x%x\n", endpoint, queueHead));
|
|
|
|
//link the td to the QH
|
|
queueHead->HW_VLink = transferDescriptor->PhysicalAddress;
|
|
queueHead->Endpoint = endpoint;
|
|
|
|
UHCD_InsertQueueHeadInSchedule(DeviceObject,
|
|
endpoint,
|
|
queueHead,
|
|
0);
|
|
|
|
UHCD_BW_Reclimation(DeviceObject, FALSE);
|
|
|
|
} else {
|
|
// could not get memory for dummy queue head,
|
|
// something is really broken.
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
UHCD_KdTrap(("failed to get memory for dummy qh\n"));
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
#ifdef USB_BIOS
|
|
|
|
// this is the Phoenix revised version
|
|
NTSTATUS
|
|
UHCD_StopBIOS(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine stops a UHCI USB BIOS if present.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - DeviceObject for this USB controller.
|
|
|
|
Return Value:
|
|
|
|
NT status code.
|
|
|
|
--*/
|
|
|
|
{
|
|
USHORT cmd;
|
|
USHORT legsup, status;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
LARGE_INTEGER startTime;
|
|
ULONG sofModifyValue = 0;
|
|
WCHAR UHCD_SofModifyKey[] = L"SofModify";
|
|
|
|
|
|
PAGED_CODE();
|
|
UHCD_KdPrint((2, "'UHCD_Stopping BIOS\n"));
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
// initilialize to whatever the BIOS set it to.
|
|
sofModifyValue =
|
|
(ULONG) (READ_PORT_UCHAR(SOF_MODIFY_REG(deviceExtension)));
|
|
|
|
//
|
|
// save current values for BIOS hand-back
|
|
//
|
|
|
|
deviceExtension->BiosCmd =
|
|
READ_PORT_USHORT(COMMAND_REG(deviceExtension));
|
|
|
|
deviceExtension->BiosIntMask =
|
|
READ_PORT_USHORT(INTERRUPT_MASK_REG(deviceExtension));
|
|
|
|
deviceExtension->BiosFrameListBase =
|
|
READ_PORT_ULONG(FRAME_LIST_BASE_REG(deviceExtension));
|
|
deviceExtension->BiosFrameListBase &= 0xfffff000;
|
|
|
|
// Grab any SOF ModifyValue indicated in the registry
|
|
|
|
//USBD_GetPdoRegistryParameter(deviceExtension->PhysicalDeviceObject,
|
|
// &sofModifyValue,
|
|
// sizeof(sofModifyValue),
|
|
// UHCD_SofModifyKey,
|
|
// sizeof(UHCD_SofModifyKey));
|
|
|
|
UHCD_GetSOFRegModifyValue(DeviceObject,
|
|
&sofModifyValue);
|
|
|
|
// save the SOF modify for posterity
|
|
deviceExtension->SavedSofModify = (CHAR) sofModifyValue;
|
|
UHCD_ASSERT(sofModifyValue <= 255);
|
|
|
|
// stop the controller,
|
|
// clear RUN bit but keep config flag set so BIOS won't reinit
|
|
cmd = READ_PORT_USHORT(COMMAND_REG(deviceExtension));
|
|
LOGENTRY(LOG_MISC, 'spBI', cmd, 0, 0);
|
|
// BUGBUG
|
|
// save cmd bits to set when we hand back
|
|
cmd &= ~(UHCD_CMD_RUN | UHCD_CMD_SW_CONFIGURED);
|
|
WRITE_PORT_USHORT(COMMAND_REG(deviceExtension), cmd);
|
|
|
|
// NOTE: if no BIOS is present
|
|
// halt bit is initially set with PIIX3
|
|
// halt bit is initially clear with VIA
|
|
|
|
// now wait for HC to halt
|
|
KeQuerySystemTime(&startTime);
|
|
for (;;) {
|
|
LARGE_INTEGER sysTime;
|
|
|
|
status = READ_PORT_USHORT(STATUS_REG(deviceExtension));
|
|
UHCD_KdPrint((2, "'STATUS = %x\n", status));
|
|
if (status & UHCD_STATUS_HCHALT) {
|
|
WRITE_PORT_USHORT(STATUS_REG(deviceExtension), 0xff);
|
|
LOGENTRY(LOG_MISC, 'spBH', cmd, 0, 0);
|
|
break;
|
|
}
|
|
|
|
KeQuerySystemTime(&sysTime);
|
|
if (sysTime.QuadPart - startTime.QuadPart > 10000) {
|
|
// time out
|
|
#if DBG
|
|
UHCD_KdPrint((1,
|
|
"'TIMEOUT HALTING CONTROLLER! (I hope you don't have USB L-BIOS)\n"));
|
|
UHCD_KdPrint((1, "'THIS IS A BIOS BUG, Port Resources @ %x Ports Available \n",
|
|
deviceExtension->Port));
|
|
//TRAP();
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
|
|
UHCD_ReadWriteConfig( deviceExtension->PhysicalDeviceObject,
|
|
TRUE,
|
|
&legsup,
|
|
0xc0, // offset of legacy bios reg
|
|
sizeof(legsup));
|
|
|
|
|
|
LOGENTRY(LOG_MISC, 'legs', legsup, 0, 0);
|
|
UHCD_KdPrint((2, "'legs = %x\n", legsup));
|
|
|
|
#ifdef JD
|
|
// TEST_TRAP();
|
|
#endif
|
|
// save it
|
|
deviceExtension->LegacySupportRegister = legsup;
|
|
if ((legsup & LEGSUP_BIOS_MODE) != 0) {
|
|
|
|
|
|
// BUGBUG save old state
|
|
deviceExtension->HcFlags |= HCFLAG_USBBIOS;
|
|
|
|
UHCD_KdPrint((1, "'*** uhcd detected a USB legacy BIOS ***\n"));
|
|
|
|
//
|
|
// if BIOS mode bits set we have to take over
|
|
//
|
|
|
|
// shut off host controller SMI enable
|
|
legsup &= ~0x10;
|
|
UHCD_ReadWriteConfig( deviceExtension->PhysicalDeviceObject,
|
|
FALSE,
|
|
&legsup,
|
|
0xc0, // offset of legacy bios reg
|
|
sizeof(legsup));
|
|
|
|
//
|
|
// BUGBUG
|
|
// out 0xff to 64h if possible
|
|
//
|
|
|
|
//
|
|
// take control
|
|
//
|
|
|
|
// read
|
|
UHCD_ReadWriteConfig( deviceExtension->PhysicalDeviceObject,
|
|
TRUE,
|
|
&legsup,
|
|
0xc0, // offset of legacy bios reg
|
|
sizeof(legsup));
|
|
|
|
legsup = LEGSUP_HCD_MODE;
|
|
//write
|
|
UHCD_ReadWriteConfig( deviceExtension->PhysicalDeviceObject,
|
|
FALSE,
|
|
&legsup,
|
|
0xc0, // offset of legacy bios reg
|
|
sizeof(legsup));
|
|
|
|
}
|
|
|
|
UHCD_KdPrint((2, "'exit UHCD_StopBIOS 0x%x\n", ntStatus));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
UHCD_StartBIOS(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine start a USB bios.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - DeviceObject for this USB controller.
|
|
|
|
Return Value:
|
|
|
|
NT status code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
USHORT cmd, legsup;
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
UHCD_KdPrint((1, "'Returning Control to USB BIOS, base port = %x\n",
|
|
deviceExtension->DeviceRegisters[0]));
|
|
|
|
// restore saved reg values
|
|
|
|
WRITE_PORT_USHORT(STATUS_REG(deviceExtension), 0xff);
|
|
|
|
WRITE_PORT_USHORT(INTERRUPT_MASK_REG(deviceExtension),
|
|
deviceExtension->BiosIntMask);
|
|
WRITE_PORT_ULONG(FRAME_LIST_BASE_REG(deviceExtension),
|
|
deviceExtension->BiosFrameListBase);
|
|
|
|
legsup = deviceExtension->LegacySupportRegister;
|
|
|
|
//write
|
|
UHCD_ReadWriteConfig(deviceExtension->PhysicalDeviceObject,
|
|
FALSE,
|
|
&legsup,
|
|
0xc0, // offset of legacy bios reg
|
|
sizeof(legsup));
|
|
|
|
|
|
// read
|
|
UHCD_ReadWriteConfig(deviceExtension->PhysicalDeviceObject,
|
|
TRUE,
|
|
&legsup,
|
|
0xc0, // offset of legacy bios reg
|
|
sizeof(legsup));
|
|
|
|
// enable SMI
|
|
legsup |= 0x10;
|
|
//write
|
|
UHCD_ReadWriteConfig(deviceExtension->PhysicalDeviceObject,
|
|
FALSE,
|
|
&legsup,
|
|
0xc0, // offset of legacy bios reg
|
|
sizeof(legsup));
|
|
|
|
cmd = deviceExtension->BiosCmd | UHCD_CMD_RUN;
|
|
WRITE_PORT_USHORT(COMMAND_REG(deviceExtension),
|
|
cmd);
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
#endif //USB_BIOS
|
|
|
|
NTSTATUS
|
|
UHCD_GetResources(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PCM_RESOURCE_LIST ResourceList,
|
|
IN OUT PUHCD_RESOURCES Resources
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - DeviceObject for this USB controller.
|
|
|
|
ResourceList - Resources for this controller.
|
|
|
|
Return Value:
|
|
|
|
NT status code.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR interrupt;
|
|
PHYSICAL_ADDRESS cardAddress;
|
|
ULONG addressSpace;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
PCM_PARTIAL_RESOURCE_LIST PartialResourceList;
|
|
PCM_FULL_RESOURCE_DESCRIPTOR fullResourceDescriptor;
|
|
BOOLEAN gotIO=FALSE, gotIRQ=FALSE;
|
|
|
|
UHCD_KdPrint((2, "'enter UHCD_GetResources\n"));
|
|
|
|
LOGENTRY(LOG_MISC, 'GRES', 0, 0, 0);
|
|
|
|
if (ResourceList == NULL) {
|
|
UHCD_KdPrint((1, "'got no resources, failing start.\n"));
|
|
return STATUS_NONE_MAPPED;
|
|
}
|
|
|
|
fullResourceDescriptor = &ResourceList->List[0];
|
|
PartialResourceList = &fullResourceDescriptor->PartialResourceList;
|
|
|
|
deviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// Initailize our page list, do this here so that UHCD_CleanupDevice can
|
|
// clean up if necessary.
|
|
//
|
|
|
|
InitializeListHead(&deviceExtension->PageList);
|
|
|
|
deviceExtension->HcFlags |= HCFLAG_NEED_CLEANUP;
|
|
|
|
for (i = 0; i < PartialResourceList->Count && NT_SUCCESS(ntStatus); i++) {
|
|
|
|
switch (PartialResourceList->PartialDescriptors[i].Type) {
|
|
case CmResourceTypePort:
|
|
|
|
//
|
|
// Set up AddressSpace to be of type Port I/O
|
|
//
|
|
|
|
UHCD_KdPrint((1, "'Port Resources Found @ %x, %d Ports Available \n",
|
|
PartialResourceList->PartialDescriptors[i].u.Port.Start.LowPart,
|
|
PartialResourceList->PartialDescriptors[i].u.Port.Length));
|
|
|
|
#if DBG
|
|
deviceExtension->Port =
|
|
PartialResourceList->PartialDescriptors[i].u.Port.Start.LowPart;
|
|
#endif
|
|
|
|
addressSpace =
|
|
(PartialResourceList->PartialDescriptors[i].Flags & CM_RESOURCE_PORT_IO) ==
|
|
CM_RESOURCE_PORT_IO? 1:0;
|
|
|
|
cardAddress=PartialResourceList->PartialDescriptors[i].u.Port.Start;
|
|
|
|
if (!addressSpace) {
|
|
deviceExtension->HcFlags |= HCFLAG_UNMAP_REGISTERS;
|
|
deviceExtension->DeviceRegisters[0] =
|
|
MmMapIoSpace(
|
|
cardAddress,
|
|
PartialResourceList->PartialDescriptors[i].u.Port.Length,
|
|
FALSE);
|
|
} else {
|
|
deviceExtension->HcFlags &= ~HCFLAG_UNMAP_REGISTERS;
|
|
deviceExtension->DeviceRegisters[0] =
|
|
(PVOID)(ULONG_PTR)cardAddress.QuadPart;
|
|
}
|
|
|
|
//
|
|
// see if we successfully mapped the IO regs
|
|
//
|
|
|
|
if (!deviceExtension->DeviceRegisters[0]) {
|
|
UHCD_KdPrint((2, "'Couldn't map the device registers. \n"));
|
|
ntStatus = STATUS_NONE_MAPPED;
|
|
|
|
} else {
|
|
UHCD_KdPrint((2, "'Mapped device registers to 0x%x.\n",
|
|
deviceExtension->DeviceRegisters[0]));
|
|
gotIO=TRUE;
|
|
deviceExtension->HcFlags |= HCFLAG_GOT_IO;
|
|
}
|
|
|
|
break;
|
|
|
|
case CmResourceTypeInterrupt:
|
|
|
|
//
|
|
// Get Vector, level, and affinity information for this interrupt.
|
|
//
|
|
|
|
UHCD_KdPrint((1, "'Interrupt Resources Found! Level = %x Vector = %x\n",
|
|
PartialResourceList->PartialDescriptors[i].u.Interrupt.Level,
|
|
PartialResourceList->PartialDescriptors[i].u.Interrupt.Vector
|
|
));
|
|
|
|
interrupt = &PartialResourceList->PartialDescriptors[i];
|
|
gotIRQ=TRUE;
|
|
|
|
//BUGBUG ??
|
|
//KeInitializeSpinLock(&DeviceExtension->InterruptSpinLock);
|
|
|
|
|
|
//DeviceExtension->InterruptMode = PartialResourceList->PartialDescriptors[i].Flags;
|
|
|
|
break;
|
|
|
|
case CmResourceTypeMemory:
|
|
|
|
//
|
|
// Set up AddressSpace to be of type Memory mapped I/O
|
|
//
|
|
|
|
UHCD_KdPrint((1, "'Memory Resources Found @ %x, Length = %x\n",
|
|
PartialResourceList->PartialDescriptors[i].u.Memory.Start.LowPart,
|
|
PartialResourceList->PartialDescriptors[i].u.Memory.Length
|
|
));
|
|
|
|
// we should never get memory resources
|
|
UHCD_KdTrap(("PnP gave us memory resources!!\n"));
|
|
|
|
break;
|
|
|
|
} /* switch */
|
|
} /* for */
|
|
|
|
//
|
|
// Sanity check that we got enough resources.
|
|
//
|
|
if (!gotIO || !gotIRQ) {
|
|
UHCD_KdPrint((1, "'Missing IO or IRQ: failing to start\n"));
|
|
ntStatus = STATUS_NONE_MAPPED;
|
|
}
|
|
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
//
|
|
// Note that we have all the resources we need
|
|
// to start
|
|
//
|
|
|
|
//
|
|
// Set up our interrupt.
|
|
//
|
|
|
|
UHCD_KdPrint((2, "'requesting interrupt vector %x level %x\n",
|
|
interrupt->u.Interrupt.Level,
|
|
interrupt->u.Interrupt.Vector));
|
|
|
|
Resources->InterruptLevel=(KIRQL)interrupt->u.Interrupt.Level;
|
|
Resources->InterruptVector=interrupt->u.Interrupt.Vector;
|
|
Resources->Affinity=interrupt->u.Interrupt.Affinity;
|
|
|
|
//
|
|
// Initialize the interrupt object for the controller.
|
|
//
|
|
|
|
deviceExtension->InterruptObject = NULL;
|
|
Resources->ShareIRQ =
|
|
interrupt->ShareDisposition == CmResourceShareShared ? TRUE : FALSE;
|
|
Resources->InterruptMode =
|
|
interrupt->Flags == CM_RESOURCE_INTERRUPT_LATCHED ? Latched : LevelSensitive;
|
|
|
|
#ifdef MAX_DEBUG
|
|
UHCD_KdPrint((2, "'interrupt->ShareDisposition %x\n", interrupt->ShareDisposition));
|
|
if (!Resources->ShareIRQ) {
|
|
TEST_TRAP();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
UHCD_KdPrint((2, "'exit UHCD_GetResources (%x)\n", ntStatus));
|
|
LOGENTRY(LOG_MISC, 'GRS1', 0, 0, ntStatus);
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
UHCD_StartDevice(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PUHCD_RESOURCES Resources
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the DeviceObject for a given instance
|
|
of a USB host controller.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - DeviceObject for this USB controller.
|
|
|
|
Resources - our assigned resources
|
|
|
|
Return Value:
|
|
|
|
NT status code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
LARGE_INTEGER deltaTime;
|
|
DEVICE_CAPABILITIES deviceCapabilities;
|
|
ULONG numberOfMapRegisters = (ULONG)-1;
|
|
DEVICE_DESCRIPTION deviceDescription;
|
|
BOOLEAN isPIIX3or4;
|
|
ULONG disableSelectiveSuspendValue = 0;
|
|
WCHAR disableSelectiveSuspendKey[] = DISABLE_SELECTIVE_SUSPEND;
|
|
WCHAR clocksPerFrameKey[] = CLOCKS_PER_FRAME;
|
|
WCHAR recClocksPerFrameKey[] = REC_CLOCKS_PER_FRAME;
|
|
LONG clocksPerFrame = 0;
|
|
LONG recClocksPerFrame = 0;
|
|
PUSBD_EXTENSION usbdExtension;
|
|
|
|
PAGED_CODE();
|
|
deviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
// get the debug options from the registry
|
|
#if DBG
|
|
UHCD_GetClassGlobalDebugRegistryParameters();
|
|
#endif
|
|
|
|
UHCD_KdPrint((2, "'enter UHCD_StartDevice\n"));
|
|
|
|
deviceExtension->HcFlags &= ~HCFLAG_HCD_STOPPED;
|
|
|
|
// enable the idle check routine
|
|
deviceExtension->HcFlags &= ~HCFLAG_DISABLE_IDLE;
|
|
|
|
LOGENTRY(LOG_MISC, 'STRT', deviceExtension->TopOfStackDeviceObject, 0, 0);
|
|
|
|
//
|
|
// sanity check our registers
|
|
//
|
|
{
|
|
USHORT cmd;
|
|
|
|
cmd = READ_PORT_USHORT(COMMAND_REG(deviceExtension));
|
|
if (cmd == 0xffff) {
|
|
UHCD_KdPrint((0, "'Controller Registers are bogus -- this is a bug\n"));
|
|
UHCD_KdPrint((0, "'Controller will FAIL to start\n"));
|
|
TRAP();
|
|
// regs are bogus -- clear the gotio flag so we don't
|
|
// try to access the registers any more
|
|
deviceExtension->HcFlags &= ~HCFLAG_GOT_IO;
|
|
ntStatus = STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(ntStatus)) {
|
|
goto UHCD_InitializeDeviceExit;
|
|
}
|
|
|
|
//
|
|
// Create our adapter object
|
|
//
|
|
|
|
RtlZeroMemory(&deviceDescription, sizeof(deviceDescription));
|
|
|
|
//BUGBUG check these
|
|
deviceDescription.Version = DEVICE_DESCRIPTION_VERSION;
|
|
deviceDescription.Master = TRUE;
|
|
deviceDescription.ScatterGather = TRUE;
|
|
deviceDescription.Dma32BitAddresses = TRUE;
|
|
|
|
deviceDescription.InterfaceType = PCIBus;
|
|
deviceDescription.DmaWidth = Width32Bits;
|
|
deviceDescription.DmaSpeed = Compatible;
|
|
|
|
deviceDescription.MaximumLength = (ULONG)-1;
|
|
|
|
deviceExtension->NumberOfMapRegisters = (ULONG)-1;
|
|
deviceExtension->AdapterObject =
|
|
IoGetDmaAdapter(deviceExtension->PhysicalDeviceObject,
|
|
&deviceDescription,
|
|
&deviceExtension->NumberOfMapRegisters);
|
|
|
|
if (deviceExtension->AdapterObject == NULL) {
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto UHCD_InitializeDeviceExit;
|
|
}
|
|
|
|
//
|
|
// We found a host controller or a host controller found us, do the final
|
|
// stages of initialization.
|
|
// Setup state variables for the host controller
|
|
//
|
|
|
|
ntStatus = UHCD_QueryCapabilities(deviceExtension->TopOfStackDeviceObject,
|
|
&deviceCapabilities);
|
|
|
|
if (!NT_SUCCESS(ntStatus)) {
|
|
goto UHCD_InitializeDeviceExit;
|
|
}
|
|
|
|
if (deviceExtension->HcFlags & HCFLAG_MAP_SX_TO_D3) {
|
|
ULONG i;
|
|
|
|
for (i=PowerSystemSleeping1; i< PowerSystemMaximum; i++) {
|
|
|
|
deviceCapabilities.DeviceState[i] = PowerDeviceD3;
|
|
|
|
UHCD_KdPrint((1, "'changing -->S%d = D%d\n", i-1,
|
|
deviceCapabilities.DeviceState[i]-1));
|
|
}
|
|
}
|
|
|
|
#if DBG
|
|
{
|
|
ULONG i;
|
|
UHCD_KdPrint((1, "'HCD Device Caps returned from PCI:\n"));
|
|
UHCD_KdPrint((1, "'\tSystemWake = S%d\n", deviceCapabilities.SystemWake-1));
|
|
UHCD_KdPrint((1, "'\tDeviceWake = D%d\n", deviceCapabilities.DeviceWake-1));
|
|
UHCD_KdPrint((1, "'\tDevice State Map:\n"));
|
|
|
|
for (i=0; i< PowerSystemHibernate; i++) {
|
|
UHCD_KdPrint((1, "'\t-->S%d = D%d\n", i-1,
|
|
deviceCapabilities.DeviceState[i]-1));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
USBD_RegisterHcDeviceCapabilities(DeviceObject,
|
|
&deviceCapabilities,
|
|
UHCD_RootHubPower);
|
|
|
|
//
|
|
// Detect the Host Controller stepping version.
|
|
//
|
|
{
|
|
UCHAR revisionId;
|
|
USHORT vendorId, deviceId;
|
|
|
|
UHCD_KdPrint((2, "'Get the stepping version\n"));
|
|
|
|
UHCD_ReadWriteConfig(deviceExtension->PhysicalDeviceObject,
|
|
TRUE,
|
|
&vendorId,
|
|
0,
|
|
sizeof(vendorId));
|
|
|
|
UHCD_ReadWriteConfig(deviceExtension->PhysicalDeviceObject,
|
|
TRUE,
|
|
&deviceId,
|
|
2,
|
|
sizeof(deviceId));
|
|
|
|
UHCD_ReadWriteConfig(deviceExtension->PhysicalDeviceObject,
|
|
TRUE,
|
|
&revisionId,
|
|
8,
|
|
sizeof(revisionId));
|
|
|
|
UHCD_KdPrint((1, "'HC vendor = %x device = %x rev = %x\n",
|
|
vendorId, deviceId, revisionId));
|
|
|
|
if (vendorId == UHCD_INTEL_VENDOR_ID &&
|
|
deviceId == UHCD_PIIX3_DEVICE_ID) {
|
|
UHCD_KdPrint((1, "'detected PIIX3\n"));
|
|
deviceExtension->ControllerType = UHCI_HW_VERSION_PIIX3;
|
|
} else if (vendorId == UHCD_INTEL_VENDOR_ID &&
|
|
deviceId == UHCD_PIIX4_DEVICE_ID) {
|
|
UHCD_KdPrint((1, "'detected PIIX4\n"));
|
|
deviceExtension->ControllerType = UHCI_HW_VERSION_PIIX4;
|
|
} else {
|
|
UHCD_KdPrint((1, "'detected unknown host controller type\n"));
|
|
deviceExtension->ControllerType = UHCI_HW_VERSION_UNKNOWN;
|
|
}
|
|
|
|
|
|
//
|
|
// is this an A1 stepping version of the piix3?
|
|
//
|
|
if (revisionId == 0 &&
|
|
vendorId == UHCD_INTEL_VENDOR_ID &&
|
|
deviceId == UHCD_PIIX3_DEVICE_ID) {
|
|
//yes, we will fail to load on the A1 systems
|
|
UHCD_KdPrint((0, "'Intel USB HC stepping version A1 is not supported\n"));
|
|
deviceExtension->SteppingVersion = UHCD_A1_STEP;
|
|
ntStatus = STATUS_UNSUCCESSFUL;
|
|
} else {
|
|
#ifdef ENABLE_B0_FEATURES
|
|
deviceExtension->SteppingVersion = UHCD_B0_STEP;
|
|
#else
|
|
deviceExtension->SteppingVersion = UHCD_A1_STEP;
|
|
#endif //ENABLE_B0_FEATURES
|
|
}
|
|
|
|
#ifdef USB_BIOS
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
ntStatus = UHCD_StopBIOS(DeviceObject);
|
|
}
|
|
#endif //USB_BIOS
|
|
}
|
|
|
|
{
|
|
USHORT legsup;
|
|
|
|
UHCD_ReadWriteConfig(deviceExtension->PhysicalDeviceObject,
|
|
TRUE,
|
|
&legsup,
|
|
0xc0, // offset of legacy bios reg
|
|
sizeof(legsup));
|
|
|
|
LOGENTRY(LOG_MISC, 'PIRd', deviceExtension, legsup, 0);
|
|
// set the PIRQD routing bit
|
|
legsup |= LEGSUP_USBPIRQD_EN;
|
|
|
|
UHCD_ReadWriteConfig( deviceExtension->PhysicalDeviceObject,
|
|
FALSE,
|
|
&legsup,
|
|
0xc0, // offset of legacy bios reg
|
|
sizeof(legsup));
|
|
}
|
|
|
|
if (!NT_SUCCESS(ntStatus)) {
|
|
goto UHCD_InitializeDeviceExit;
|
|
}
|
|
|
|
ntStatus = UHCD_InitializeSchedule(DeviceObject);
|
|
|
|
LOGENTRY(LOG_MISC, 'INIs', 0, 0, ntStatus);
|
|
if (!NT_SUCCESS(ntStatus)) {
|
|
UHCD_KdPrint((0, "'InitializeSchedule Failed! %x\n", ntStatus));
|
|
goto UHCD_InitializeDeviceExit;
|
|
}
|
|
|
|
//
|
|
// initialization all done, last step is to connect the interrupt & DPCs.
|
|
//
|
|
|
|
KeInitializeDpc(&deviceExtension->IsrDpc,
|
|
UHCD_IsrDpc,
|
|
DeviceObject);
|
|
|
|
UHCD_KdPrint((2, "'requesting interrupt vector %x level %x\n",
|
|
Resources->InterruptLevel,
|
|
Resources->InterruptVector));
|
|
|
|
ntStatus = IoConnectInterrupt(
|
|
&(deviceExtension->InterruptObject),
|
|
(PKSERVICE_ROUTINE) UHCD_InterruptService,
|
|
(PVOID) DeviceObject,
|
|
(PKSPIN_LOCK)NULL,
|
|
Resources->InterruptVector,
|
|
Resources->InterruptLevel,
|
|
Resources->InterruptLevel,
|
|
Resources->InterruptMode,
|
|
Resources->ShareIRQ,
|
|
Resources->Affinity,
|
|
FALSE); // BUGBUG FloatingSave, this is configurable
|
|
|
|
LOGENTRY(LOG_MISC, 'IOCi', 0, 0, ntStatus);
|
|
if (!NT_SUCCESS(ntStatus)) {
|
|
UHCD_KdPrint((0, "'IoConnectInterrupt Failed! %x\n", ntStatus));
|
|
goto UHCD_InitializeDeviceExit;
|
|
}
|
|
|
|
// consider ourselves 'ON'
|
|
deviceExtension->CurrentDevicePowerState = PowerDeviceD0;
|
|
|
|
//
|
|
// Initialize the controller registers.
|
|
// NOTE:
|
|
// We don't do reset until the interrrupt is hooked because
|
|
// the HC for some reason likes to generate an interrupt
|
|
// (USBINT) when it is reset.
|
|
//
|
|
|
|
ntStatus = UHCD_StartGlobalReset(DeviceObject);
|
|
|
|
LOGENTRY(LOG_MISC, 'GLBr', 0, 0, ntStatus);
|
|
if (!NT_SUCCESS(ntStatus)) {
|
|
UHCD_KdPrint((0, "'GlobalReset Failed! %x\n", ntStatus));
|
|
goto UHCD_InitializeDeviceExit;
|
|
}
|
|
//
|
|
// Everything is set, we need to wait for the
|
|
// global reset of the Host controller to complete.
|
|
//
|
|
|
|
// 20 ms to reset...
|
|
deltaTime.QuadPart = -10000 * 20;
|
|
|
|
//
|
|
// block here until reset is complete
|
|
//
|
|
|
|
(VOID) KeDelayExecutionThread(KernelMode,
|
|
FALSE,
|
|
&deltaTime);
|
|
|
|
ntStatus = UHCD_CompleteGlobalReset(DeviceObject);
|
|
|
|
// 10 ms for devices to recover
|
|
// BUGBUG seems the Philips speakers need this
|
|
// deltaTime.QuadPart = -10000 * 1000;
|
|
//
|
|
// (VOID) KeDelayExecutionThread(KernelMode,
|
|
// FALSE,
|
|
// &deltaTime);
|
|
|
|
if (!NT_SUCCESS(ntStatus)) {
|
|
goto UHCD_InitializeDeviceExit;
|
|
}
|
|
|
|
//
|
|
// bus reset complete, now activate the root hub emulation
|
|
//
|
|
#ifdef ROOT_HUB
|
|
UHCD_KdPrint((2, "'Initialize Root Hub\n"));
|
|
|
|
//
|
|
// BUGBUG hard coded to two ports
|
|
//
|
|
isPIIX3or4 = (deviceExtension->ControllerType == UHCI_HW_VERSION_PIIX3) ||
|
|
(deviceExtension->ControllerType == UHCI_HW_VERSION_PIIX4);
|
|
|
|
USBD_GetPdoRegistryParameter(deviceExtension->PhysicalDeviceObject,
|
|
&disableSelectiveSuspendValue,
|
|
sizeof(disableSelectiveSuspendValue),
|
|
disableSelectiveSuspendKey,
|
|
sizeof(disableSelectiveSuspendKey));
|
|
|
|
USBD_GetPdoRegistryParameter(deviceExtension->PhysicalDeviceObject,
|
|
&recClocksPerFrame,
|
|
sizeof(recClocksPerFrame),
|
|
recClocksPerFrameKey,
|
|
sizeof(recClocksPerFrameKey));
|
|
|
|
deviceExtension->RegRecClocksPerFrame =
|
|
recClocksPerFrame;
|
|
|
|
if ((deviceExtension->RootHub =
|
|
RootHub_Initialize(DeviceObject,
|
|
2,
|
|
(BOOLEAN)(!isPIIX3or4 &&
|
|
!disableSelectiveSuspendValue))) == NULL) {
|
|
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
#if DBG
|
|
if (!isPIIX3or4 && !disableSelectiveSuspendValue) {
|
|
UHCD_KdPrint ((1, "'selective suspend enabled\n"));
|
|
} else {
|
|
UHCD_KdPrint ((1, "'selective suspend disabled\n"));
|
|
}
|
|
#endif
|
|
|
|
// HACK - Tell USBD if this HC is PIIX3 or PIIX4.
|
|
|
|
if (isPIIX3or4) {
|
|
usbdExtension = (PUSBD_EXTENSION)deviceExtension;
|
|
usbdExtension->IsPIIX3or4 = TRUE;
|
|
}
|
|
|
|
// END HACK
|
|
|
|
deviceExtension->RootHubTimersActive = 0;
|
|
#endif //ROOT_HUB
|
|
|
|
//
|
|
// our current power state is 'ON'
|
|
//
|
|
deviceExtension->CurrentDevicePowerState = PowerDeviceD0;
|
|
|
|
// this will disable the controller if nothing
|
|
// is initailly connected to the ports
|
|
KeQuerySystemTime(&deviceExtension->LastIdleTime);
|
|
deviceExtension->IdleTime = 100000000;
|
|
deviceExtension->XferIdleTime = 0;
|
|
|
|
UHCD_InitializeDeviceExit:
|
|
|
|
if (!NT_SUCCESS(ntStatus)) {
|
|
#ifdef MAX_DEBUG
|
|
TEST_TRAP();
|
|
#endif
|
|
UHCD_KdPrint((2, "'Initialization Failed, cleaning up \n"));
|
|
|
|
//
|
|
// The initialization failed. Cleanup resources before exiting.
|
|
//
|
|
//
|
|
// Note: No need/way to undo the KeInitializeDpc or
|
|
// KeInitializeTimer calls.
|
|
//
|
|
|
|
UHCD_CleanupDevice(DeviceObject);
|
|
|
|
}
|
|
|
|
UHCD_KdPrint((2, "'exit UHCD_StartDevice (%x)\n", ntStatus));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
UHCD_DeferredStartDevice(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - DeviceObject for this USB controller.
|
|
|
|
Return Value:
|
|
|
|
NT Status code.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS ntStatus;
|
|
UHCD_RESOURCES resources;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
ULONG forceLowPowerState = 0;
|
|
|
|
PAGED_CODE();
|
|
|
|
deviceExtension = DeviceObject->DeviceExtension;
|
|
// initailize extension here
|
|
deviceExtension->HcFlags = 0;
|
|
|
|
UHCD_GetClassGlobalRegistryParameters(&forceLowPowerState);
|
|
|
|
if (forceLowPowerState) {
|
|
deviceExtension->HcFlags |= HCFLAG_MAP_SX_TO_D3;
|
|
}
|
|
|
|
|
|
irpStack = IoGetCurrentIrpStackLocation (Irp);
|
|
|
|
ntStatus = UHCD_GetResources(DeviceObject,
|
|
irpStack->Parameters.StartDevice.AllocatedResourcesTranslated,
|
|
&resources);
|
|
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
ntStatus = UHCD_StartDevice(DeviceObject,
|
|
&resources);
|
|
}
|
|
|
|
//
|
|
// Set the IRP status because USBD doesn't
|
|
//
|
|
|
|
Irp->IoStatus.Status = ntStatus;
|
|
|
|
LOGENTRY(LOG_MISC, 'dfST', 0, 0, ntStatus);
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
UHCD_UpdateFrameCounter(
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Starts the USB host controller executing the schedule.
|
|
Start Controller is called in by KeSynchronizeExecution.
|
|
|
|
Arguments:
|
|
|
|
Context - DeviceData for this USB controller.
|
|
|
|
Return Value:
|
|
|
|
TRUE
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
ULONG frameNumber;
|
|
ULONG currentFrame, highPart;
|
|
|
|
deviceExtension = Context;
|
|
|
|
// This code maintains the 32-bit frame counter
|
|
|
|
frameNumber = (ULONG) READ_PORT_USHORT(FRAME_LIST_CURRENT_INDEX_REG(deviceExtension));
|
|
|
|
// did the sign bit change ?
|
|
if ((deviceExtension->LastFrame ^ frameNumber) & 0x0400) {
|
|
// Yes
|
|
deviceExtension->FrameHighPart += 0x0800 -
|
|
((frameNumber ^ deviceExtension->FrameHighPart) & 0x0400);
|
|
}
|
|
|
|
// remember the last frame number
|
|
deviceExtension->LastFrame = frameNumber;
|
|
|
|
// calc frame number and update las frame processed
|
|
// if necseesary
|
|
|
|
highPart = deviceExtension->FrameHighPart;
|
|
|
|
// get 11-bit frame number, high 17-bits are 0
|
|
//frameNumber = (ULONG) READ_PORT_USHORT(FRAME_LIST_CURRENT_INDEX_REG(deviceExtension));
|
|
|
|
currentFrame = ((frameNumber & 0x0bff) | highPart) +
|
|
((frameNumber ^ highPart) & 0x0400);
|
|
|
|
//UHCD_KdPrint((2, "'currentFrame2 = %x\n", currentFrame));
|
|
|
|
if (deviceExtension->HcFlags & HCFLAG_ROLLOVER_IDLE) {
|
|
deviceExtension->LastFrameProcessed = currentFrame - 1;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
UHCD_DisableIdleCheck(
|
|
IN PDEVICE_EXTENSION DeviceExtension
|
|
)
|
|
{
|
|
KIRQL irql;
|
|
|
|
KeAcquireSpinLock(&DeviceExtension->HcFlagSpin, &irql);
|
|
|
|
DeviceExtension->HcFlags |= HCFLAG_DISABLE_IDLE;
|
|
|
|
KeReleaseSpinLock(&DeviceExtension->HcFlagSpin, irql);
|
|
}
|
|
|
|
VOID
|
|
UHCD_WakeIdle(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
takes the controller out of the idle state
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL irql;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
LOGENTRY(LOG_MISC, 'wIDL', DeviceObject, 0, 0);
|
|
|
|
KeRaiseIrql(DISPATCH_LEVEL, &irql);
|
|
deviceExtension->XferIdleTime = 0;
|
|
UHCD_CheckIdle(DeviceObject);
|
|
KeLowerIrql(irql);
|
|
}
|
|
|
|
|
|
VOID
|
|
UHCD_CheckIdle(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
If the controllers hub ports are not connected this function
|
|
stops the host controller
|
|
|
|
If there are no iso enpoints open then this function
|
|
disables the rollover interrupt
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - DeviceObject of the controller to stop
|
|
|
|
Return Value:
|
|
|
|
NT status code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
BOOLEAN portsIdle = TRUE;
|
|
USHORT cmd;
|
|
LARGE_INTEGER timeNow;
|
|
BOOLEAN fastIsoEndpointListEmpty;
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
fastIsoEndpointListEmpty = IsListEmpty(&deviceExtension->FastIsoEndpointList);
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&deviceExtension->HcFlagSpin);
|
|
|
|
if (deviceExtension->HcFlags & HCFLAG_DISABLE_IDLE) {
|
|
goto UHCD_CheckIdle_Done;
|
|
}
|
|
|
|
UHCD_ASSERT(deviceExtension->InterruptObject);
|
|
if (!KeSynchronizeExecution(deviceExtension->InterruptObject,
|
|
UHCD_UpdateFrameCounter,
|
|
deviceExtension)) {
|
|
TRAP(); //something has gone terribly wrong
|
|
}
|
|
|
|
portsIdle = RootHub_PortsIdle(deviceExtension->RootHub) &&
|
|
IsListEmpty(&deviceExtension->EndpointList);
|
|
|
|
if (portsIdle) {
|
|
|
|
if (!(deviceExtension->HcFlags & HCFLAG_IDLE)) {
|
|
// we are not idle,
|
|
// see how long ports have been idle if it
|
|
// is longer then 10 seconds stop the
|
|
// controller
|
|
KeQuerySystemTime(&timeNow);
|
|
|
|
if (deviceExtension->IdleTime == 0) {
|
|
KeQuerySystemTime(&deviceExtension->LastIdleTime);
|
|
deviceExtension->IdleTime = 1;
|
|
}
|
|
|
|
deviceExtension->IdleTime +=
|
|
(LONG) (timeNow.QuadPart -
|
|
deviceExtension->LastIdleTime.QuadPart);
|
|
deviceExtension->LastIdleTime = timeNow;
|
|
|
|
// ports are idle stop the controller
|
|
if (// 10 seconds in 100ns units
|
|
deviceExtension->IdleTime > 100000000) {
|
|
|
|
cmd = READ_PORT_USHORT(
|
|
COMMAND_REG(deviceExtension)) & ~UHCD_CMD_RUN;
|
|
WRITE_PORT_USHORT(COMMAND_REG(deviceExtension), cmd);
|
|
|
|
deviceExtension->HcFlags |= HCFLAG_IDLE;
|
|
deviceExtension->IdleTime = 0;
|
|
|
|
LOGENTRY(LOG_MISC, 'sOFF', DeviceObject, 0, 0);
|
|
|
|
UHCD_KdPrint((2, "'HC stopped\n"));
|
|
|
|
}
|
|
}
|
|
|
|
} else {
|
|
// ports are active start the controller
|
|
deviceExtension->IdleTime = 0;
|
|
if (deviceExtension->HcFlags & HCFLAG_IDLE) {
|
|
|
|
UHCD_KdPrint((2, "'ports active, break idle\n"));
|
|
|
|
// reset the frame list current index
|
|
WRITE_PORT_USHORT(
|
|
FRAME_LIST_CURRENT_INDEX_REG(deviceExtension), 0);
|
|
|
|
// re-initialize internal frame counters.
|
|
deviceExtension->FrameHighPart =
|
|
deviceExtension->LastFrame = 0;
|
|
|
|
|
|
cmd = READ_PORT_USHORT(
|
|
COMMAND_REG(deviceExtension)) | UHCD_CMD_RUN;
|
|
WRITE_PORT_USHORT(COMMAND_REG(deviceExtension), cmd);
|
|
|
|
LOGENTRY(LOG_MISC, 's-ON', DeviceObject, 0, 0);
|
|
|
|
deviceExtension->HcFlags &= ~HCFLAG_IDLE;
|
|
|
|
UHCD_KdPrint((2, "'HC restart\n"));
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// now deal with the HC rollover interrupt
|
|
//
|
|
|
|
if (!(deviceExtension->HcFlags & HCFLAG_ROLLOVER_IDLE)) {
|
|
|
|
//
|
|
// rollover ints are on,
|
|
// see how long it has been since the last data transfer
|
|
//
|
|
|
|
KeQuerySystemTime(&timeNow);
|
|
|
|
if (deviceExtension->XferIdleTime == 0) {
|
|
KeQuerySystemTime(&deviceExtension->LastXferIdleTime);
|
|
deviceExtension->XferIdleTime = 1;
|
|
}
|
|
|
|
deviceExtension->XferIdleTime +=
|
|
(LONG) (timeNow.QuadPart -
|
|
deviceExtension->LastXferIdleTime.QuadPart);
|
|
deviceExtension->LastXferIdleTime = timeNow;
|
|
}
|
|
|
|
if (deviceExtension->XferIdleTime > 100000000 &&
|
|
!fastIsoEndpointListEmpty) {
|
|
|
|
// are we currently idle
|
|
|
|
if (!(deviceExtension->HcFlags & HCFLAG_ROLLOVER_IDLE)) {
|
|
//
|
|
// No
|
|
//
|
|
// if we have seen no data transfers submitted
|
|
// for 10 seconds disable the rollover interrupt
|
|
//
|
|
|
|
// turn off the interrupts for rollover
|
|
deviceExtension->HcFlags |= HCFLAG_ROLLOVER_IDLE;
|
|
|
|
deviceExtension->TriggerTDList->TDs[0].InterruptOnComplete =
|
|
deviceExtension->TriggerTDList->TDs[1].InterruptOnComplete = 0;
|
|
|
|
//UHCD_KdPrint((2, "UHCD: HC idle ints stopped\n"));
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// this will turn on the rollover interrupts
|
|
// when we see transfers
|
|
//
|
|
|
|
// are we currently idle
|
|
|
|
if (deviceExtension->HcFlags & HCFLAG_ROLLOVER_IDLE) {
|
|
|
|
//
|
|
// Yes
|
|
//
|
|
|
|
UHCD_KdPrint((2, "'activate rollover ints\n"));
|
|
|
|
deviceExtension->TriggerTDList->TDs[0].InterruptOnComplete =
|
|
deviceExtension->TriggerTDList->TDs[1].InterruptOnComplete = 1;
|
|
|
|
deviceExtension->HcFlags &= ~HCFLAG_ROLLOVER_IDLE;
|
|
|
|
//UHCD_KdPrint((2, "UHCD: HC idle ints started\n"));
|
|
}
|
|
}
|
|
|
|
UHCD_CheckIdle_Done:
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&deviceExtension->HcFlagSpin);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
UHCD_GetSOFRegModifyValue(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN OUT PULONG SofModifyValue
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - DeviceObject for this USB controller.
|
|
|
|
Return Value:
|
|
|
|
NT Status code.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
LONG clocksPerFrame = 12000;
|
|
LONG recClocksPerFrame;
|
|
LONG sofModify;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
|
|
PAGED_CODE();
|
|
|
|
// the default
|
|
*SofModifyValue = 64;
|
|
|
|
deviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
recClocksPerFrame =
|
|
deviceExtension->RegRecClocksPerFrame;
|
|
|
|
if (recClocksPerFrame && clocksPerFrame) {
|
|
sofModify = recClocksPerFrame - clocksPerFrame + 64;
|
|
*SofModifyValue = sofModify;
|
|
}
|
|
|
|
UHCD_KdPrint((1, "'Clocks/Frame %d Recommended Clocks/Frame %d SofModify %d\n",
|
|
clocksPerFrame, recClocksPerFrame, *SofModifyValue));
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
UHCD_GetConfigValue(
|
|
IN PWSTR ValueName,
|
|
IN ULONG ValueType,
|
|
IN PVOID ValueData,
|
|
IN ULONG ValueLength,
|
|
IN PVOID Context,
|
|
IN PVOID EntryContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is a callback routine for RtlQueryRegistryValues
|
|
It is called for each entry in the Parameters
|
|
node to set the config values. The table is set up
|
|
so that this function will be called with correct default
|
|
values for keys that are not present.
|
|
|
|
Arguments:
|
|
|
|
ValueName - The name of the value (ignored).
|
|
ValueType - The type of the value
|
|
ValueData - The data for the value.
|
|
ValueLength - The length of ValueData.
|
|
Context - A pointer to the CONFIG structure.
|
|
EntryContext - The index in Config->Parameters to save the value.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
|
|
UHCD_KdPrint((2, "'Type 0x%x, Length 0x%x\n", ValueType, ValueLength));
|
|
|
|
switch (ValueType) {
|
|
case REG_DWORD:
|
|
RtlCopyMemory(EntryContext, ValueData, sizeof(ULONG));
|
|
break;
|
|
case REG_BINARY:
|
|
// BUGBUG we are only set up to read a byte
|
|
RtlCopyMemory(EntryContext, ValueData, 1);
|
|
break;
|
|
default:
|
|
ntStatus = STATUS_INVALID_PARAMETER;
|
|
}
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
UHCD_SetControllerD0(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
/* ++
|
|
*
|
|
* Description:
|
|
*
|
|
* Work item scheduled to do processing at passive
|
|
* level when the controller is put in D0 by PNP/POWER.
|
|
*
|
|
*
|
|
* Arguments:
|
|
*
|
|
* Return:
|
|
*
|
|
* -- */
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
//if (deviceExtension->CurrentDevicePowerState == PowerDeviceD0) {
|
|
// TEST_TRAP();
|
|
// return;
|
|
//}
|
|
|
|
LOGENTRY(LOG_MISC, 'POWC', deviceExtension->CurrentDevicePowerState,
|
|
DeviceObject, 0);
|
|
|
|
switch (deviceExtension->CurrentDevicePowerState) {
|
|
case PowerDeviceD3:
|
|
|
|
UHCD_KdPrint((2, "'PowerDeviceD0 (ON) from (OFF)\n"));
|
|
deviceExtension->CurrentDevicePowerState = PowerDeviceD0;
|
|
break;
|
|
|
|
case PowerDeviceD1:
|
|
case PowerDeviceD2:
|
|
UHCD_KdPrint((2, "'PowerDeviceD0 (ON) from (SUSPEND)\n"));
|
|
break;
|
|
case PowerDeviceD0:
|
|
// probably a bug in the kernel/configmg or usbd
|
|
UHCD_KdPrint((2, "'PowerDeviceD0 (ON) from (ON)\n"));
|
|
break;
|
|
} /* case */
|
|
|
|
#ifdef USB_BIOS
|
|
//if (deviceExtension->HcFlags & HCFLAG_USBBIOS) {
|
|
UHCD_StopBIOS(DeviceObject);
|
|
//}
|
|
#endif //USB_BIOS
|
|
{
|
|
USHORT legsup;
|
|
|
|
UHCD_ReadWriteConfig(deviceExtension->PhysicalDeviceObject,
|
|
TRUE,
|
|
&legsup,
|
|
0xc0, // offset of legacy bios reg
|
|
sizeof(legsup));
|
|
|
|
LOGENTRY(LOG_MISC, 'PIRd', deviceExtension, legsup, 0);
|
|
// set the PIRQD routing bit
|
|
legsup |= LEGSUP_USBPIRQD_EN;
|
|
|
|
UHCD_ReadWriteConfig( deviceExtension->PhysicalDeviceObject,
|
|
FALSE,
|
|
&legsup,
|
|
0xc0, // offset of legacy bios reg
|
|
sizeof(legsup));
|
|
}
|
|
|
|
deviceExtension->CurrentDevicePowerState = PowerDeviceD0;
|
|
UHCD_KdPrint((1, " Host Controller entered (D0)\n"));
|
|
|
|
}
|
|
|
|
#if DBG
|
|
|
|
NTSTATUS
|
|
UHCD_GetClassGlobalDebugRegistryParameters(
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS ntStatus;
|
|
RTL_QUERY_REGISTRY_TABLE QueryTable[3];
|
|
PWCHAR usb = L"usb";
|
|
extern ULONG UHCD_Debug_Trace_Level;
|
|
extern ULONG UHCD_W98_Debug_Trace;
|
|
extern ULONG UHCD_Debug_Asserts;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Set up QueryTable to do the following:
|
|
//
|
|
|
|
// spew level
|
|
QueryTable[0].QueryRoutine = UHCD_GetConfigValue;
|
|
QueryTable[0].Flags = 0;
|
|
QueryTable[0].Name = DEBUG_LEVEL;
|
|
QueryTable[0].EntryContext = &UHCD_Debug_Trace_Level;
|
|
QueryTable[0].DefaultType = REG_DWORD;
|
|
QueryTable[0].DefaultData = &UHCD_Debug_Trace_Level;
|
|
QueryTable[0].DefaultLength = sizeof(UHCD_Debug_Trace_Level);
|
|
|
|
// ntkern trace buffer
|
|
QueryTable[1].QueryRoutine = UHCD_GetConfigValue;
|
|
QueryTable[1].Flags = 0;
|
|
QueryTable[1].Name = DEBUG_WIN9X;
|
|
QueryTable[1].EntryContext = &UHCD_W98_Debug_Trace;
|
|
QueryTable[1].DefaultType = REG_DWORD;
|
|
QueryTable[1].DefaultData = &UHCD_W98_Debug_Trace;
|
|
QueryTable[1].DefaultLength = sizeof(UHCD_W98_Debug_Trace);
|
|
|
|
//
|
|
// Stop
|
|
//
|
|
QueryTable[2].QueryRoutine = NULL;
|
|
QueryTable[2].Flags = 0;
|
|
QueryTable[2].Name = NULL;
|
|
|
|
ntStatus = RtlQueryRegistryValues(
|
|
RTL_REGISTRY_SERVICES,
|
|
usb,
|
|
QueryTable, // QueryTable
|
|
NULL, // Context
|
|
NULL); // Environment
|
|
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
UHCD_KdPrint((1, "'Debug Trace Level Set: (%d)\n", UHCD_Debug_Trace_Level));
|
|
|
|
if (UHCD_W98_Debug_Trace) {
|
|
UHCD_KdPrint((1, "'NTKERN Trace is ON\n"));
|
|
} else {
|
|
UHCD_KdPrint((1, "'NTKERN Trace is OFF\n"));
|
|
}
|
|
|
|
if (UHCD_Debug_Trace_Level > 0) {
|
|
ULONG UHCD_Debug_Asserts = 1;
|
|
}
|
|
}
|
|
|
|
if ( STATUS_OBJECT_NAME_NOT_FOUND == ntStatus ) {
|
|
ntStatus = STATUS_SUCCESS;
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
#endif
|
|
|
|
NTSTATUS
|
|
UHCD_GetClassGlobalRegistryParameters(
|
|
IN OUT PULONG ForceLowPowerState
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS ntStatus;
|
|
UCHAR toshibaLegacyFlags = 0;
|
|
RTL_QUERY_REGISTRY_TABLE QueryTable[2];
|
|
PWCHAR usb = L"class\\usb";
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Set up QueryTable to do the following:
|
|
//
|
|
|
|
// force power mapping
|
|
QueryTable[0].QueryRoutine = UHCD_GetConfigValue;
|
|
QueryTable[0].Flags = 0;
|
|
QueryTable[0].Name = L"ForceLowPowerState";
|
|
QueryTable[0].EntryContext = ForceLowPowerState;
|
|
QueryTable[0].DefaultType = REG_DWORD;
|
|
QueryTable[0].DefaultData = ForceLowPowerState;
|
|
QueryTable[0].DefaultLength = sizeof(*ForceLowPowerState);
|
|
|
|
//
|
|
// Stop
|
|
//
|
|
QueryTable[1].QueryRoutine = NULL;
|
|
QueryTable[1].Flags = 0;
|
|
QueryTable[1].Name = NULL;
|
|
|
|
ntStatus = RtlQueryRegistryValues(
|
|
// RTL_REGISTRY_ABSOLUTE, // RelativeTo
|
|
RTL_REGISTRY_SERVICES,
|
|
// UnicodeRegistryPath->Buffer, // Path
|
|
usb,
|
|
QueryTable, // QueryTable
|
|
NULL, // Context
|
|
NULL); // Environment
|
|
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
UHCD_KdPrint(( 0, "'HC ForceLowPower = 0x%x\n",
|
|
*ForceLowPowerState));
|
|
}
|
|
|
|
if ( STATUS_OBJECT_NAME_NOT_FOUND == ntStatus ) {
|
|
ntStatus = STATUS_SUCCESS;
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
#if 0
|
|
// not used
|
|
NTSTATUS
|
|
UHCD_GetGlobalRegistryParameters(
|
|
IN OUT PULONG DisableController
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS ntStatus;
|
|
UCHAR toshibaLegacyFlags = 0;
|
|
RTL_QUERY_REGISTRY_TABLE QueryTable[2];
|
|
PWCHAR usb = L"usb";
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Set up QueryTable to do the following:
|
|
//
|
|
|
|
// legacy flag
|
|
QueryTable[0].QueryRoutine = UHCD_GetConfigValue;
|
|
QueryTable[0].Flags = 0;
|
|
QueryTable[0].Name = L"DisablePIIXUsb";
|
|
QueryTable[0].EntryContext = DisableController;
|
|
QueryTable[0].DefaultType = REG_BINARY;
|
|
QueryTable[0].DefaultData = DisableController;
|
|
QueryTable[0].DefaultLength = sizeof(*DisableController);
|
|
|
|
//
|
|
// Stop
|
|
//
|
|
QueryTable[1].QueryRoutine = NULL;
|
|
QueryTable[1].Flags = 0;
|
|
QueryTable[1].Name = NULL;
|
|
|
|
ntStatus = RtlQueryRegistryValues(
|
|
// RTL_REGISTRY_ABSOLUTE, // RelativeTo
|
|
RTL_REGISTRY_SERVICES,
|
|
// UnicodeRegistryPath->Buffer,// Path
|
|
usb,
|
|
QueryTable, // QurryTable
|
|
NULL, // Context
|
|
NULL); // Environment
|
|
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
UHCD_KdPrint(( 0, "'HC Disable flag = 0x%x\n",
|
|
*DisableController));
|
|
}
|
|
|
|
if ( STATUS_OBJECT_NAME_NOT_FOUND == ntStatus ) {
|
|
ntStatus = STATUS_SUCCESS;
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
#endif
|