windows-nt/Source/XPSP1/NT/drivers/wdm/usb/hcd/openhci/ohciroot.c
2020-09-26 16:20:57 +08:00

955 lines
33 KiB
C

/*++
Copyright (c) 1995 Microsoft Corporation
Module Name:
OHCIroot.c
Abstract:
The module manages the Root Hub of the OpenHCI controller.
Environment:
kernel mode only
Notes:
Revision History:
03-22-96 : created jfuller
05-25-96 : reworked kenray
--*/
#include "OpenHCI.h"
// This is the data packet that the upper levels of the stack believe
// are sending to a real hub. We intercept and interpret this packet.
typedef struct _SETUP_PACKET {
union {
struct {
UCHAR Recipient:
5; // entity to recieve command
UCHAR Type:
2; // type of command
UCHAR DirectionIn:
1; // data transfer direction
};
struct {
UCHAR RequestType; // request type code
};
};
UCHAR Request; // command code
USHORT wValue; // parameter
USHORT wIndex; // another parameter
USHORT wLength; // transfer length (same as
// TransferBufferLength?)
} SETUP_PACKET, *PSETUP_PACKET;
#define SPrequest_GetStatus 0x0
#define SPrequest_ClearFeature 0x1
#define SPrequest_SetFeature 0x3
#define SPrequest_SetAddress 0x5
#define SPrequest_GetDescriptor 0x6
#define SPrequest_SetDescriptor 0x7
#define SPrequest_GetConfiguration 0x8
#define SPrequest_SetConfiguration 0x9
#define SPrequest_GetInterface 0xA
#define SPrequest_SetInterface 0xB
#define SPrequest_SyncFrame 0xC
#define SPrecipient_Device 0
#define SPrecipient_Interface 1
#define SPrecipient_Endpoint 2
#define SPrecipient_Other 3
#define SPtype_Standard 0
#define SPtype_Class 1
#define SPtype_Vendor 2
#define SPbmrt_In_Standard_Device 0x80 // 1000 0000
#define SPbmrt_In_Standard_Interface 0x81 // 1000 0001
#define SPbmrt_In_Standard_Endpoint 0x82 // 1000 0010
#define SPbmrt_Out_Standard_Device 0x00 // 0000 0000
#define SPbmrt_Out_Standard_Interface 0x01 // 0000 0001
#define SPbmrt_Out_Standard_Endpoint 0x02 // 0000 0010
#define SPbmrt_In_Class_Device 0xA0 // 1010 0000
#define SPbmrt_In_Class_Other 0xA3 // 1010 0011
#define SPbmrt_Out_Class_Device 0x20 // 0010 0000
#define SPbmrt_Out_Class_Other 0x23 // 0010 0011
//
// Hub Class Feature Selectors, see Table 11-13 in the
// Universal Serial Bus Specification Revision 1.0
//
//
// Recipient Hub
//
#define C_HUB_LOCAL_POWER 0
#define C_HUB_OVER_CURRENT 1
//
// Recipient Port
//
#define PORT_CONNECTION 0
#define PORT_ENABLE 1
#define PORT_SUSPEND 2
#define PORT_OVER_CURRENT 3
#define PORT_RESET 4
#define PORT_POWER 8
#define PORT_LOW_SPEED 9
#define C_PORT_CONNECTION 16
#define C_PORT_ENABLE 17
#define C_PORT_SUSPEND 18
#define C_PORT_OVER_CURRENT 19
#define C_PORT_RESET 20
// default descriptors for root hub
UCHAR RH_DeviceDescriptor[] = {0x12, //bLength
0x01, //bDescrpitorType
0x00, 0x01, //bcdUSB
0x09, //bDeviceClass
0x01, //bDeviceSubClass
0x00, //bDeviceProtocol
0x08, //bMaxPacketSize0
0x00, 0x00, //idVendor
0x00, 0x00, //idProduct
0x00, 0x00, //bcdDevice
0x00, //iManufacturer
0x00, //iProduct
0x00, //iSerialNumber
0x01};//bNumConfigurations
UCHAR RH_ConfigurationDescriptor[] =
/* Config Descriptor */
{0x09, //bLength
0x02, //bDescriptorType
0x19, 0x00, //wTotalLength
0x01, //bNumInterfaces
0x01, //iConfigurationValue
0x00, //iConfiguration
0x40, //bmAttributes
0x00, //MaxPower
/* Interface Descriptor */
0x09, //bLength
0x04, //bDescriptorType
0x00, //bInterfaceNumber
0x00, //bAlternateSetting
0x01, //bNumEndpoints
0x09, //bInterfaceClass
0x01, //bInterfaceSubClass
0x00, //bInterfaceProtocol
0x00, //iInterface
/* Endpoint Descriptor */
0x07, //bLength
0x05, //bDescriptorType
0x81, //bEndpointAddress
0x03, //bmAttributes
0x08, 0x00, //wMaxPacketSize
0x0a};//bInterval
UCHAR RH_HubDescriptor[] =
{0x09, //bLength
0x29, //bDescriptorType
0x02, //bNbrPorts
0x00, 0x00, //wHubCharacteristics
0x00, // bPwrOn2PwrGood
0x00}; // bHubContrCurrent
void OpenHCI_CancelRootInterrupt(PDEVICE_OBJECT, PIRP);
NTSTATUS
OpenHCI_RootHubStartXfer(
PDEVICE_OBJECT DeviceObject,
PHCD_DEVICE_DATA DeviceData,
PIRP Irp,
PHCD_URB UsbRequest,
PHCD_ENDPOINT Endpoint
)
{
PSETUP_PACKET Pkt;
PCHAR Buffer;
KIRQL oldIrql;
PHC_OPERATIONAL_REGISTER HC = DeviceData->HC;
struct _URB_HCD_COMMON_TRANSFER *trans =
&UsbRequest->HcdUrbCommonTransfer;
ASSERT(NULL == Endpoint->HcdED);
if (Endpoint->Type == USB_ENDPOINT_TYPE_CONTROL
&& Endpoint->EndpointNumber == 0) {
//
// This is the default endpoint of the root hub
//
Pkt = (PSETUP_PACKET) & trans->Extension.u.SetupPacket[0];
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_RH_TRACE,
("'RootHub emulation: %02x %02x %04x %04x %04x\n",
Pkt->RequestType, Pkt->Request,
Pkt->wValue, Pkt->wIndex,
Pkt->wLength));
ASSERT(trans->TransferBufferLength == Pkt->wLength);
if (trans->TransferBufferLength) {
// This is cause an exception if any of the upper levels of the
// USB stack (those that created this MDL) are misbehaving.
Buffer = MmGetSystemAddressForMdl(trans->TransferBufferMDL);
} else {
Buffer = NULL;
}
switch (Pkt->Request) {
case SPrequest_GetStatus:
switch (Pkt->RequestType) {
case SPbmrt_In_Class_Device:
//
// GetHubStatus
//
if (4 != trans->TransferBufferLength) {
TRAP();
break;
}
*((PULONG) Buffer) = ~HcRhS_DeviceRemoteWakeupEnable
& READ_REGISTER_ULONG(&HC->HcRhStatus);
trans->Status = USBD_STATUS_SUCCESS;
return STATUS_SUCCESS;
case SPbmrt_In_Class_Other:
//
// GetPortStatus
//
if (4 != trans->TransferBufferLength) {
TRAP();
break;
}
*((PULONG) Buffer) = ReadPortStatusFix(DeviceData,
Pkt->wIndex - 1);
LOGENTRY(G, 'gPRT', Pkt->wIndex, trans->TransferBufferLength, *((PULONG) Buffer));
trans->TransferBufferLength = 4;
trans->Status = USBD_STATUS_SUCCESS;
return STATUS_SUCCESS;
case SPbmrt_In_Standard_Device:
if (0 == Pkt->wIndex && 2 == trans->TransferBufferLength) {
Buffer[0] = 3; // Remote Wakeup & Self Powered
Buffer[1] = 0;
return STATUS_SUCCESS;
}
break;
case SPbmrt_In_Standard_Interface:
if (0 == Pkt->wIndex && 2 == trans->TransferBufferLength) {
Buffer[0] = Buffer[0] = 0;
return STATUS_SUCCESS;
}
break;
case SPbmrt_In_Standard_Endpoint:
if ((2 == trans->TransferBufferLength)
&& ((1 == Pkt->wIndex) || (0 == Pkt->wIndex))) {
Buffer[0] = Buffer[0] = 0;
return STATUS_SUCCESS;
}
break;
}
break;
case SPrequest_ClearFeature:
if (SPbmrt_Out_Class_Other == Pkt->RequestType) {
//
// clear port feature
//
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_RH_TRACE,
("'Clear Feature wValue = %x index = %x\n",
Pkt->wValue, Pkt->wIndex));
switch (Pkt->wValue) {
case PORT_ENABLE:
WRITE_REGISTER_ULONG(&HC->HcRhPortStatus[Pkt->wIndex - 1],
HcRhPS_ClearPortEnable);
break;
case PORT_SUSPEND:
WRITE_REGISTER_ULONG(&HC->HcRhPortStatus[Pkt->wIndex - 1],
HcRhPS_ClearPortSuspend);
break;
case PORT_POWER:
WRITE_REGISTER_ULONG(&HC->HcRhPortStatus[Pkt->wIndex - 1],
HcRhPS_ClearPortPower);
break;
case C_PORT_CONNECTION:
WRITE_REGISTER_ULONG(&HC->HcRhPortStatus[Pkt->wIndex - 1],
HcRhPS_ClearConnectStatusChange);
#if FAKEPORTCHANGE
DeviceData->FakePortChange &= ~(1 << (Pkt->wIndex - 1));
#endif
break;
case C_PORT_ENABLE:
WRITE_REGISTER_ULONG(&HC->HcRhPortStatus[Pkt->wIndex - 1],
HcRhPS_ClearPortEnableStatusChange);
break;
case C_PORT_SUSPEND:
WRITE_REGISTER_ULONG(&HC->HcRhPortStatus[Pkt->wIndex - 1],
HcRhPS_ClearPortSuspendStatusChange);
break;
case C_PORT_OVER_CURRENT:
WRITE_REGISTER_ULONG(&HC->HcRhPortStatus[Pkt->wIndex - 1],
HcRhPS_ClearPortOverCurrentChange);
break;
case C_PORT_RESET:
WRITE_REGISTER_ULONG(&HC->HcRhPortStatus[Pkt->wIndex - 1],
HcRhPS_ClearPortResetStatusChange);
break;
default:
break;
}
trans->TransferBufferLength = 0;
trans->Status = USBD_STATUS_SUCCESS;
return STATUS_SUCCESS;
} else if (SPbmrt_Out_Class_Device == Pkt->RequestType) {
//
// clear hub feature
//
switch (Pkt->wValue) {
case C_HUB_LOCAL_POWER:
// The OpenHCI Root Hub does not support the local power
// status feature. The Local Power Status Change bit is
// always read as '0'. See section 7.4.3 hcRhStatus in
// the OpenHCI specification.
break;
case C_HUB_OVER_CURRENT:
WRITE_REGISTER_ULONG(&HC->HcRhStatus, HcRhS_ClearOverCurrentIndicatorChange);
break;
default:
break;
}
trans->TransferBufferLength = 0;
trans->Status = USBD_STATUS_SUCCESS;
return STATUS_SUCCESS;
}
break;
case SPrequest_SetFeature:
if (SPbmrt_Out_Class_Other == Pkt->RequestType) {
//
// set port feature
//
switch (Pkt->wValue) {
case PORT_ENABLE:
WRITE_REGISTER_ULONG(&HC->HcRhPortStatus[Pkt->wIndex - 1],
HcRhPS_SetPortEnable);
break;
case PORT_SUSPEND:
WRITE_REGISTER_ULONG(&HC->HcRhPortStatus[Pkt->wIndex - 1],
HcRhPS_SetPortSuspend);
break;
case PORT_RESET:
WRITE_REGISTER_ULONG(&HC->HcRhPortStatus[Pkt->wIndex - 1],
HcRhPS_SetPortReset);
break;
case PORT_POWER:
WRITE_REGISTER_ULONG(&HC->HcRhPortStatus[Pkt->wIndex - 1],
HcRhPS_SetPortPower);
break;
}
trans->TransferBufferLength = 0;
trans->Status = USBD_STATUS_SUCCESS;
return STATUS_SUCCESS;
} else if (SPbmrt_Out_Class_Device == Pkt->RequestType) {
//
// set hub feature (no hub features can be set)
//
switch (Pkt->wValue) {
default:
break;
}
trans->TransferBufferLength = 0;
trans->Status = USBD_STATUS_SUCCESS;
return STATUS_SUCCESS;
}
break;
case SPrequest_SetAddress:
;
if (SPbmrt_Out_Standard_Device == Pkt->RequestType) {
if (DeviceData->RootHubAddress != (char) Pkt->wValue) {
Endpoint->EpFlags &= ~EP_ROOT_HUB;
ASSERT(NULL == DeviceData->RootHubInterrupt);
//
// If you change the address while there is an open
// endpoint
// to the root hub interrupt than you strand that
// endpoint.
// You really should close this endpoint before changing
// the
// address.
//
// if (DeviceData->RootHubInterrupt)
// {
// ExFreePool (DeviceData->RootHubInterrupt);
// DeviceData->RootHubInterrupt = 0;
// }
}
DeviceData->RootHubAddress = (char) Pkt->wValue;
trans->TransferBufferLength = 0;
trans->Status = USBD_STATUS_SUCCESS;
return STATUS_SUCCESS;
}
break;
case SPrequest_GetDescriptor:
{
union {
UCHAR rc[4];
ULONG rl;
USHORT rs;
} ports;
ULONG length;
switch (Pkt->RequestType) {
case SPbmrt_In_Class_Device:
{
UCHAR scratch[100];
PUSB_HUB_DESCRIPTOR hubDescriptor;
HC_RH_DESCRIPTOR_A descrA;
//
// Get Descriptor, Hub
//
OHCI_ASSERT(sizeof(scratch)>sizeof(RH_HubDescriptor));
hubDescriptor = (PUSB_HUB_DESCRIPTOR) scratch;
RtlCopyMemory(scratch,
RH_HubDescriptor,
sizeof(RH_DeviceDescriptor));
descrA.ul =
READ_REGISTER_ULONG(&HC->HcRhDescriptorA.ul);
// this will set
// bNumberOfPorts, wHubCharacteristics & bPowerOnToPowerGood
RtlCopyMemory(&hubDescriptor->bNumberOfPorts,
&descrA,
sizeof(descrA));
LOGENTRY(G, 'rhCH', DeviceData, descrA.ul, 0);
hubDescriptor->bHubControlCurrent = 0;
ports.rl = READ_REGISTER_ULONG(&HC->HcRhDescriptorB.ul);
if (hubDescriptor->bNumberOfPorts < 8) {
hubDescriptor->bDescriptorLength = 9;
hubDescriptor->bRemoveAndPowerMask[0] = ports.rc[0];
hubDescriptor->bRemoveAndPowerMask[1] = ports.rc[2];
} else {
hubDescriptor->bDescriptorLength = 11;
RtlCopyMemory(&hubDescriptor->bRemoveAndPowerMask[0],
&ports,
sizeof(ports));
}
trans->TransferBufferLength
= MIN(hubDescriptor->bDescriptorLength, trans->TransferBufferLength);
RtlCopyMemory(Buffer, scratch, trans->TransferBufferLength);
trans->Status = USBD_STATUS_SUCCESS;
return STATUS_SUCCESS;
}
case SPbmrt_In_Standard_Device:
{
PUSB_DEVICE_DESCRIPTOR deviceDescriptor;
PUSB_CONFIGURATION_DESCRIPTOR configDescriptor;
UCHAR scratch[100];
OHCI_ASSERT(sizeof(scratch)>sizeof(RH_DeviceDescriptor));
OHCI_ASSERT(sizeof(scratch)>sizeof(RH_ConfigurationDescriptor));
//
// Get Descriptor
//
switch(Pkt->wValue) {
case 0x100:
{ // Device Descriptor
deviceDescriptor = (PUSB_DEVICE_DESCRIPTOR) scratch;
RtlCopyMemory(scratch,
RH_DeviceDescriptor,
sizeof(RH_DeviceDescriptor));
length =
MIN(sizeof(RH_DeviceDescriptor), Pkt->wLength);
deviceDescriptor->idVendor =
DeviceData->VendorID;
deviceDescriptor->idProduct =
DeviceData->DeviceID;
// deviceDescriptor->bcdDevice =
// DeviceData->RevisionID;
}
break;
case 0x200:
{ // Configuration Descriptor
configDescriptor = (PUSB_CONFIGURATION_DESCRIPTOR) scratch;
RtlCopyMemory(scratch,
RH_ConfigurationDescriptor,
sizeof(RH_ConfigurationDescriptor));
length = MIN(sizeof(RH_ConfigurationDescriptor),
Pkt->wLength);
}
break;
default:
TRAP(); // should not get here, if we do then
// it is probably a bug in the hub driver
trans->Status = USBD_STATUS_STALL_PID;
trans->TransferBufferLength = 0; // No data transferred
return STATUS_IO_DEVICE_ERROR;
}
RtlCopyMemory(Buffer, scratch, length);
trans->TransferBufferLength = length;
trans->Status = USBD_STATUS_SUCCESS;
return STATUS_SUCCESS;
}
default:
;
}
break;
}
case SPrequest_GetConfiguration:
if (SPbmrt_In_Standard_Device == Pkt->RequestType &&
1 == trans->TransferBufferLength) {
*Buffer = (CHAR) DeviceData->RootHubConfig;
trans->Status = USBD_STATUS_SUCCESS;
return STATUS_SUCCESS;
}
break;
case SPrequest_SetConfiguration:
if (SPbmrt_Out_Standard_Device == Pkt->RequestType &&
0 == trans->TransferBufferLength &&
0 == (Pkt->wValue & ~1)) { // Only configs of 0 and 1
// supported.
DeviceData->RootHubConfig = (char) Pkt->wValue;
trans->Status = USBD_STATUS_SUCCESS;
return STATUS_SUCCESS;
}
case SPrequest_SetDescriptor:
case SPrequest_GetInterface: // We support only one interface.
case SPrequest_SetInterface: // We support only one interface.
case SPrequest_SyncFrame:
default:
;
}
trans->Status = USBD_STATUS_STALL_PID;
trans->TransferBufferLength = 0; // No data transferred
return STATUS_IO_DEVICE_ERROR;
} else if (Endpoint->Type == USB_ENDPOINT_TYPE_INTERRUPT
&& Endpoint->EndpointNumber == 1
&& DeviceData->RootHubConfig != 0) {
NTSTATUS status;
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_RH_TRACE,
("'***** Insert REQUEST for Interrupt \n\n"));
ASSERT(DeviceData->RootHubInterrupt == Endpoint);
status = CheckRootHub(DeviceData,
HC,
UsbRequest);
if (status == STATUS_PENDING) {
KeAcquireSpinLock(&Endpoint->QueueLock, &oldIrql);
trans->Status = HCD_PENDING_STATUS_QUEUED;
InsertTailList(&Endpoint->RequestQueue, &trans->hca.HcdListEntry);
KeReleaseSpinLock(&Endpoint->QueueLock, oldIrql);
if (NULL == trans->UrbLink) {
Irp->IoStatus.Status = STATUS_PENDING;
IoMarkIrpPending(Irp);
IoSetCancelRoutine(Irp, OpenHCI_CancelRootInterrupt);
if (Irp->Cancel) {
struct _URB_HCD_COMMON_TRANSFER *nextTrans;
if (IoSetCancelRoutine(Irp, NULL)) {
while (trans) {
nextTrans = &trans->UrbLink->HcdUrbCommonTransfer;
KeAcquireSpinLock(&Endpoint->QueueLock, &oldIrql);
RemoveEntryList(&trans->hca.HcdListEntry);
trans->Status = USBD_STATUS_CANCELED;
KeReleaseSpinLock(&Endpoint->QueueLock, oldIrql);
trans = nextTrans;
}
OpenHCI_CompleteIrp(DeviceObject, Irp, STATUS_CANCELLED);
return STATUS_CANCELLED;
} else {
return STATUS_CANCELLED;
}
}
}
WRITE_REGISTER_ULONG(&HC->HcInterruptEnable, HcInt_RootHubStatusChange);
}
return status;
}
//
// Unknown transfer to root hub
//
trans->Status = USBD_STATUS_DEV_NOT_RESPONDING;
trans->TransferBufferLength = 0;
return STATUS_IO_DEVICE_ERROR;
}
ULONG
OpenHCI_BuildRootHubStatusChange(
PHCD_DEVICE_DATA DeviceData,
PHC_OPERATIONAL_REGISTER HC
)
/*++
Routine Description:
Builds and returns a bitmap of the current root hub status changes.
Bit 0 = Hub change detected
Bit N = Port N change detected, where port N is one-based, not zero-based
--*/
{
ULONG changes;
ULONG portIndex;
ULONG mask;
ULONG portStatus;
// Loop over the ports and build a status change mask for each
// each port that has a current status change.
//
changes = 0;
for (portIndex = 0, mask = (1 << 1);
portIndex < DeviceData->NumberOfPorts;
portIndex++, mask <<= 1)
{
// Read the current port status
//
portStatus = ReadPortStatusFix(DeviceData, portIndex);
// Keep only the current port status change bits
//
portStatus &= (HcRhPS_ConnectStatusChange |
HcRhPS_PortEnableStatusChange |
HcRhPS_PortSuspendStatusChange |
HcRhPS_OverCurrentIndicatorChange |
HcRhPS_PortResetStatusChange);
if (portStatus)
{
changes |= mask;
}
LOGENTRY(G, 'prtS', changes, portStatus, portIndex);
}
// Read the current status of the hub itself
//
portStatus = READ_REGISTER_ULONG(&HC->HcRhStatus);
// Keep only the current hub status change bits
//
portStatus &= (HcRhS_LocalPowerStatusChange |
HcRhS_OverCurrentIndicatorChange);
if (portStatus)
{
changes |= 1; // Bit 0 is for the hub itself.
}
return changes;
}
void
EmulateRootHubInterruptXfer(
PHCD_DEVICE_DATA DeviceData,
PHC_OPERATIONAL_REGISTER HC
)
/*++
Routine Description:
Emulates an interupt pipe to the Root Hubs #1 Endpoint.
We Pop an entry off of the URB request queue and satisfy
it with the current state of the Root hub status registers.
If there is no outstanding request then disable this
interrupt.
--*/
{
PHCD_ENDPOINT Endpoint = DeviceData->RootHubInterrupt;
PLIST_ENTRY entry;
KIRQL oldIrql;
PHCD_URB UsbRequest;
ULONG changes;
PCHAR buffer;
PDRIVER_CANCEL oldCancel;
struct _URB_HCD_COMMON_TRANSFER *trans;
// mask off the interrupt status change
WRITE_REGISTER_ULONG(&HC->HcInterruptStatus, HcInt_RootHubStatusChange);
// Get a bitmap of the current root hub status changes
//
changes = OpenHCI_BuildRootHubStatusChange(DeviceData, HC);
if (0 == changes) {
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_RH_TRACE,
("'RH Interrupt no change \n\n"));
return;
}
KeAcquireSpinLock(&Endpoint->QueueLock, &oldIrql);
if (IsListEmpty(&Endpoint->RequestQueue)) {
// disable interrupts for rh status change
WRITE_REGISTER_ULONG(&HC->HcInterruptDisable,
HcInt_RootHubStatusChange);
KeReleaseSpinLock(&Endpoint->QueueLock, oldIrql);
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_RH_ERROR,
("'*******WARNING*****\n"));
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_RH_ERROR,
("'!!! RH Interrupt no IRP QUEUED \n"));
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_RH_ERROR,
("'Disable RH interrupt\n"));
return;
}
entry = RemoveHeadList(&Endpoint->RequestQueue);
KeReleaseSpinLock(&Endpoint->QueueLock, oldIrql);
UsbRequest = CONTAINING_RECORD(entry,
HCD_URB,
HcdUrbCommonTransfer.hca.HcdListEntry);
trans = &UsbRequest->HcdUrbCommonTransfer;
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_RH_TRACE,
("'RH Interrupt completing URB %x\n", UsbRequest));
if (trans->TransferBufferLength &&
(buffer = MmGetSystemAddressForMdl(trans->TransferBufferMDL)) != NULL)
{
RtlCopyMemory(buffer, &changes, trans->TransferBufferLength);
}
else
{
ASSERT(0);
}
trans->Status = USBD_STATUS_SUCCESS;
if (0 == trans->UrbLink) {
oldCancel = IoSetCancelRoutine(trans->hca.HcdIrp, NULL);
if (oldCancel) {
OpenHCI_CompleteIrp(DeviceData->DeviceObject,
trans->hca.HcdIrp,
USBD_STATUS_SUCCESS);
}
}
}
NTSTATUS
CheckRootHub(PHCD_DEVICE_DATA DeviceData,
PHC_OPERATIONAL_REGISTER HC,
PHCD_URB UsbRequest
)
/*++
Routine Description:
before an interrupt transfer is queued this function checks the change
bits in the root hub registers, if any are set the it processes the request
immediately.
--*/
{
PHCD_ENDPOINT Endpoint = DeviceData->RootHubInterrupt;
ULONG changes;
PCHAR buffer;
PDRIVER_CANCEL oldCancel;
struct _URB_HCD_COMMON_TRANSFER *trans;
// Get a bitmap of the current root hub status changes
//
changes = OpenHCI_BuildRootHubStatusChange(DeviceData, HC);
if (0 == changes) {
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_RH_TRACE,
("'RH Interrupt no change \n\n"));
return STATUS_PENDING;
}
trans = &UsbRequest->HcdUrbCommonTransfer;
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_RH_TRACE,
("'RH Interrupt completing URB %x\n", UsbRequest));
if (trans->TransferBufferLength &&
(buffer = MmGetSystemAddressForMdl(trans->TransferBufferMDL)) != NULL)
{
RtlCopyMemory(buffer, &changes, trans->TransferBufferLength);
}
else
{
ASSERT(0);
}
trans->Status = USBD_STATUS_SUCCESS;
if (0 == trans->UrbLink) {
oldCancel = IoSetCancelRoutine(trans->hca.HcdIrp, NULL);
// complete the request
return STATUS_SUCCESS;
}
return STATUS_PENDING;
}
void
OpenHCI_CancelRootInterrupt(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
)
/*++
Routine Description:
cancel an Irp queued for Interrupt Transfer.
Args:
DeviceObject the object to which the irp was originally directed.
Irp the poor doomed packet.
--*/
{
PHCD_DEVICE_DATA DeviceData;
PHCD_ENDPOINT Endpoint;
KIRQL oldIrql;
BOOLEAN lastURB;
struct _URB_HCD_COMMON_TRANSFER *Trans;
DeviceData = (PHCD_DEVICE_DATA) DeviceObject->DeviceExtension;
ASSERT(TRUE == Irp->Cancel);
Trans = &((PHCD_URB) URB_FROM_IRP(Irp))->HcdUrbCommonTransfer;
ASSERT(URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER == Trans->Function);
LOGENTRY(G, 'rCan', Irp, Trans, Trans->Status);
Endpoint = Trans->hca.HcdEndpoint;
ASSERT(Endpoint->EpFlags & EP_ROOT_HUB);
ASSERT(URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER == Trans->Function);
ASSERT(Trans->hca.HcdEndpoint == Endpoint);
ASSERT(Trans->UrbLink == NULL);
ASSERT(USBD_STATUS_CANCELING != Trans->Status);
if (USBD_STATUS_CANCELED != Trans->Status)
{
KeAcquireSpinLock(&Endpoint->QueueLock, &oldIrql);
// the item may have already been removed
if (Trans->hca.HcdListEntry.Flink != NULL)
{
OHCI_ASSERT(Trans->hca.HcdListEntry.Blink != NULL);
RemoveEntryList(&Trans->hca.HcdListEntry);
}
Trans->Status = USBD_STATUS_CANCELED;
KeReleaseSpinLock(&Endpoint->QueueLock, oldIrql);
}
IoReleaseCancelSpinLock(Irp->CancelIrql);
OpenHCI_CompleteIrp(DeviceObject, Irp, STATUS_CANCELLED);
}
ULONG
ReadPortStatusFix(
PHCD_DEVICE_DATA DeviceData,
ULONG PortIndex
)
/*++
Routine Description:
Reads a HcRhPortStatus register, working around some known hardware
bugs in early revs of the AMD K7 chipset.
When a Port Status read is performed while Viper is mastering the PCI
bus during ED & TD reads, the Port Status read may return either all
'0's or an address of an ED or TD (upper bits will match the HcHCCA
base address).
Quick & Dirty fix:
If a Port status read data has '1's in its upper reserved bits, the
read is invalid and should be discarded. If a Port status read
returns all '0's, then it is safe to read it a few more times, and if
the status is truly 00h, then the reads should all return 00h.
Otherwise, the 00h was an invalid read and the data which is
subsequently returned (with '0's in its reserved bits) is valid.
--*/
{
PULONG pulRegister;
ULONG ulRegVal;
int x;
pulRegister = &DeviceData->HC->HcRhPortStatus[PortIndex];
for (x = 0; x < 10; x++)
{
ulRegVal = READ_REGISTER_ULONG(pulRegister);
if ((ulRegVal) && (!(ulRegVal & HcRhPS_RESERVED)))
{
break;
}
else
{
KeStallExecutionProcessor(5);
}
}
#if FAKEPORTCHANGE
#pragma message("NOTE: enabling fake port change hack")
if (DeviceData->FakePortChange & (1 << PortIndex))
{
ulRegVal |= HcRhPS_ConnectStatusChange;
}
if (DeviceData->FakePortDisconnect & (1 << PortIndex))
{
ulRegVal &= ~HcRhPS_CurrentConnectStatus;
}
#endif
return ulRegVal;
}