955 lines
33 KiB
C
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;
|
|
}
|