2029 lines
58 KiB
C
2029 lines
58 KiB
C
/*++
|
||
|
||
Copyright (c) 1995 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
ohciurb.c
|
||
|
||
Abstract:
|
||
|
||
The module manages transactions on the USB.
|
||
|
||
Environment:
|
||
|
||
kernel mode only
|
||
|
||
Notes:
|
||
|
||
Revision History:
|
||
|
||
02-07-96: created jfuller
|
||
03-05-96: in work kenray
|
||
|
||
--*/
|
||
|
||
#include "openhci.h"
|
||
|
||
/*
|
||
Private functions to this module
|
||
*/
|
||
NTSTATUS
|
||
OpenHCI_OpenEndpoint(
|
||
IN PDEVICE_OBJECT,
|
||
IN PIRP,
|
||
PHCD_DEVICE_DATA,
|
||
PHCD_URB);
|
||
|
||
NTSTATUS
|
||
OpenHCI_CloseEndpoint(
|
||
IN PDEVICE_OBJECT,
|
||
IN PIRP,
|
||
PHCD_DEVICE_DATA,
|
||
PHCD_URB);
|
||
|
||
VOID
|
||
OpenHCI_CompleteIrp(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN NTSTATUS ntStatus
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Completes an I/O Request
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - pointer to a device object
|
||
|
||
Irp - pointer to an I/O Request Packet to complete
|
||
|
||
ntStatus - status code to set int the IRP when completed
|
||
|
||
Return Value:
|
||
|
||
|
||
--*/
|
||
{
|
||
PHCD_DEVICE_DATA DeviceData;
|
||
|
||
DeviceData = DeviceObject->DeviceExtension;
|
||
|
||
Irp->IoStatus.Status = ntStatus;
|
||
Irp->IoStatus.Information = 0;
|
||
|
||
//
|
||
// call IoCompleteRequest thru USBD to give it a chance to do
|
||
// cleanup and error mapping
|
||
//
|
||
|
||
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_CALL_TRACE,
|
||
("'Completing IRP %x (%x)\n", Irp, ntStatus));
|
||
|
||
IRP_OUT(Irp);
|
||
LOGENTRY(G, 'Cirp', Irp, 0, ntStatus);
|
||
USBD_CompleteRequest(Irp, IO_NO_INCREMENT);
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
OpenHCI_URB_Dispatch(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Process URBs from the dispatch routine.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - pointer to a device object
|
||
|
||
Irp - pointer to an I/O Request Packet
|
||
|
||
Return Value:
|
||
|
||
|
||
--*/
|
||
{
|
||
PHCD_URB urb;
|
||
NTSTATUS ntStatus = STATUS_SUCCESS;
|
||
PHCD_DEVICE_DATA DeviceData;
|
||
PHCD_ENDPOINT endpoint;
|
||
struct _URB_HCD_ENDPOINT_STATE *state;
|
||
PHC_OPERATIONAL_REGISTER HC;
|
||
|
||
DeviceData = (PHCD_DEVICE_DATA) DeviceObject->DeviceExtension;
|
||
OpenHCI_KdPrintDD(DeviceData,
|
||
OHCI_DBG_CALL_TRACE, ("'enter URB_Dispatch \n"));
|
||
|
||
HC = DeviceData->HC;
|
||
|
||
urb = (PHCD_URB) URB_FROM_IRP(Irp);
|
||
|
||
switch (urb->UrbHeader.Function) {
|
||
|
||
//
|
||
// Open Endpoint and Close Endpoint IRPs are serialized
|
||
// within USBD so we can execute them now.
|
||
//
|
||
case URB_FUNCTION_HCD_OPEN_ENDPOINT:
|
||
ntStatus = OpenHCI_OpenEndpoint(DeviceObject, Irp, DeviceData, urb);
|
||
OpenHCI_CompleteIrp(DeviceObject, Irp, ntStatus);
|
||
break;
|
||
|
||
case URB_FUNCTION_HCD_CLOSE_ENDPOINT:
|
||
ntStatus = OpenHCI_CloseEndpoint(DeviceObject, Irp, DeviceData, urb);
|
||
OpenHCI_CompleteIrp(DeviceObject, Irp, ntStatus);
|
||
break;
|
||
|
||
case URB_FUNCTION_CONTROL_TRANSFER:
|
||
case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER:
|
||
case URB_FUNCTION_ISOCH_TRANSFER:
|
||
|
||
endpoint = urb->HcdUrbCommonTransfer.hca.HcdEndpoint;
|
||
|
||
ntStatus = OpenHCI_QueueTransfer(DeviceObject, Irp);
|
||
|
||
if (ntStatus != STATUS_PENDING) {
|
||
OpenHCI_CompleteIrp(DeviceObject, Irp, ntStatus);
|
||
}
|
||
break;
|
||
|
||
case URB_FUNCTION_GET_CURRENT_FRAME_NUMBER:
|
||
urb->UrbGetCurrentFrameNumber.FrameNumber
|
||
= Get32BitFrameNumber(DeviceData);
|
||
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_CALL_INFO,
|
||
("'Get32BitFrameNumber: %x",
|
||
Get32BitFrameNumber(DeviceData)));
|
||
urb->UrbHeader.Status = USBD_STATUS_SUCCESS;
|
||
OpenHCI_CompleteIrp(DeviceObject, Irp, ntStatus);
|
||
break;
|
||
|
||
case URB_FUNCTION_SET_FRAME_LENGTH:
|
||
{
|
||
HC_FM_INTERVAL interval;
|
||
|
||
//get the current value
|
||
interval.ul =
|
||
READ_REGISTER_ULONG(&HC->HcFmInterval.ul);
|
||
|
||
interval.FrameInterval +=
|
||
(CHAR) urb->UrbSetFrameLength.FrameLengthDelta;
|
||
|
||
WRITE_REGISTER_ULONG(&HC->HcFmInterval.ul,
|
||
interval.ul);
|
||
|
||
urb->UrbHeader.Status = USBD_STATUS_SUCCESS;
|
||
OpenHCI_CompleteIrp(DeviceObject, Irp, ntStatus);
|
||
}
|
||
break;
|
||
|
||
case URB_FUNCTION_GET_FRAME_LENGTH:
|
||
{
|
||
HC_FM_INTERVAL interval;
|
||
|
||
//get the current value
|
||
interval.ul =
|
||
READ_REGISTER_ULONG(&HC->HcFmInterval.ul);
|
||
|
||
urb->UrbGetFrameLength.FrameNumber =
|
||
Get32BitFrameNumber(DeviceData);
|
||
urb->UrbGetFrameLength.FrameLength =
|
||
interval.FrameInterval;
|
||
|
||
urb->UrbHeader.Status = USBD_STATUS_SUCCESS;
|
||
OpenHCI_CompleteIrp(DeviceObject, Irp, ntStatus);
|
||
}
|
||
break;
|
||
|
||
|
||
case URB_FUNCTION_HCD_GET_ENDPOINT_STATE:
|
||
{
|
||
KIRQL irql;
|
||
BOOLEAN queuedTransfers, activeTransfers;
|
||
|
||
state = &urb->HcdUrbEndpointState;
|
||
endpoint = state->HcdEndpoint;
|
||
|
||
LOGENTRY(G, 'gEPS', Irp, endpoint, 0);
|
||
|
||
if (endpoint == ZERO_LOAD_ENDPOINT(DeviceData)) {
|
||
state->HcdEndpointState = 0;
|
||
} else {
|
||
|
||
ASSERT_ENDPOINT(endpoint);
|
||
|
||
OpenHCI_LockAndCheckEndpoint(endpoint,
|
||
&queuedTransfers,
|
||
&activeTransfers,
|
||
&irql);
|
||
|
||
if (endpoint->EpFlags & EP_ROOT_HUB) {
|
||
state->HcdEndpointState =
|
||
(queuedTransfers | activeTransfers)
|
||
? HCD_ENDPOINT_TRANSFERS_QUEUED : 0;
|
||
} else {
|
||
state->HcdEndpointState =
|
||
((endpoint->HcdED->HcED.HeadP & HcEDHeadP_HALT)
|
||
? HCD_ENDPOINT_HALTED : 0)
|
||
| ((queuedTransfers | activeTransfers)
|
||
? HCD_ENDPOINT_TRANSFERS_QUEUED : 0);
|
||
}
|
||
|
||
OpenHCI_UnlockEndpoint(endpoint,
|
||
irql);
|
||
|
||
}
|
||
urb->UrbHeader.Status = USBD_STATUS_SUCCESS;
|
||
LOGENTRY(G, 'GETs', Irp, endpoint, state->HcdEndpointState);
|
||
OpenHCI_CompleteIrp(DeviceObject, Irp, ntStatus);
|
||
}
|
||
break;
|
||
|
||
case URB_FUNCTION_HCD_SET_ENDPOINT_STATE:
|
||
{
|
||
PHC_ENDPOINT_DESCRIPTOR hcED;
|
||
|
||
state = &urb->HcdUrbEndpointState;
|
||
endpoint = state->HcdEndpoint;
|
||
ASSERT_ENDPOINT(endpoint);
|
||
|
||
hcED = &endpoint->HcdED->HcED;
|
||
LOGENTRY(G, 'SETs', Irp, endpoint, state->HcdEndpointState);
|
||
|
||
// reset some endpoint flags
|
||
SET_EPFLAG(endpoint, EP_VIRGIN);
|
||
|
||
// need to reset data toggle on clear halt
|
||
if (state->HcdEndpointState & HCD_ENDPOINT_RESET_DATA_TOGGLE) {
|
||
//OHCI_ASSERT(hcED->sKip ||
|
||
// (hcED->HeadP & HcEDHeadP_HALT));
|
||
hcED->HeadP = (endpoint->HcdHeadP->PhysicalAddress
|
||
& ~HcEDHeadP_CARRY);
|
||
}
|
||
|
||
if (!(state->HcdEndpointState & HCD_ENDPOINT_HALTED)) {
|
||
|
||
LOGENTRY(G, 'CLRh', Irp, endpoint, state->HcdEndpointState);
|
||
|
||
if (hcED->HeadP & HcEDHeadP_HALT) {
|
||
// If the endpoint is indeed halted than the hardware will
|
||
// not be touching the ED. So we should be able to set this
|
||
// flag with impunity.
|
||
hcED->HeadP &= ~HcEDHeadP_HALT;
|
||
LOGENTRY(G, 'HLTn', Irp, endpoint, state->HcdEndpointState);
|
||
}
|
||
|
||
ENABLE_LIST(DeviceData->HC, endpoint);
|
||
}
|
||
}
|
||
urb->UrbHeader.Status = USBD_STATUS_SUCCESS;
|
||
OpenHCI_CompleteIrp(DeviceObject, Irp, ntStatus);
|
||
break;
|
||
|
||
case URB_FUNCTION_HCD_ABORT_ENDPOINT:
|
||
|
||
endpoint = urb->HcdUrbAbortEndpoint.HcdEndpoint;
|
||
LOGENTRY(G, 'Abrt', Irp, endpoint, 0);
|
||
if (ZERO_LOAD_ENDPOINT(DeviceData) == endpoint) {
|
||
ntStatus = STATUS_SUCCESS;
|
||
} else {
|
||
KIRQL oldIrq;
|
||
|
||
KeAcquireSpinLock(&DeviceData->PausedSpin, &oldIrq);
|
||
#if DBG
|
||
if (endpoint->EpFlags & EP_ROOT_HUB) {
|
||
OHCI_ASSERT(endpoint->AbortIrp == NULL);
|
||
}
|
||
#endif
|
||
if (endpoint->AbortIrp != NULL) {
|
||
// if we get an abort while we still have an abort irp
|
||
// pending then the driver has a bug, we will just fail
|
||
// the request
|
||
TRAP();
|
||
ntStatus = STATUS_UNSUCCESSFUL;
|
||
KeReleaseSpinLock(&DeviceData->PausedSpin, oldIrq);
|
||
OpenHCI_CompleteIrp(DeviceObject, Irp, ntStatus);
|
||
} else {
|
||
KeReleaseSpinLock(&DeviceData->PausedSpin, oldIrq);
|
||
ntStatus = OpenHCI_AbortEndpoint(DeviceObject,
|
||
Irp,
|
||
DeviceData,
|
||
urb);
|
||
}
|
||
}
|
||
|
||
break;
|
||
|
||
default:
|
||
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_CALL_ERROR,
|
||
("'OpenHCI_URB_Dispatch -- invalid URB function (%x)\n",
|
||
urb->UrbHeader.Function));
|
||
urb->UrbHeader.Status = USBD_STATUS_INVALID_URB_FUNCTION;
|
||
OpenHCI_CompleteIrp(DeviceObject, Irp, ntStatus);
|
||
}
|
||
|
||
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_CALL_TRACE,
|
||
("'exit OpenHCI_URB_Dispatch (%x)\n", ntStatus));
|
||
|
||
return ntStatus;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
OpenHCI_GrowDescriptorPool (
|
||
IN PHCD_DEVICE_DATA DeviceData,
|
||
IN ULONG ReserveLength,
|
||
OUT PCHAR *VirtAddr OPTIONAL,
|
||
OUT PHYSICAL_ADDRESS *PhysAddr OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Reserve Transfer/Endpoint Descriptors.
|
||
|
||
This function allocates a block of common buffer memory for
|
||
Transfer and Endpoint Descriptors and puts it on a tracking
|
||
list in the device extension.
|
||
|
||
Arguments:
|
||
|
||
DeviceData - pointer to a device extension
|
||
|
||
ReserveLength - amount of space to reserve at the beginning
|
||
of the common buffer
|
||
|
||
VirtAddr - virtual address of space reserved at the beginning
|
||
of the common buffer
|
||
|
||
PhysAddr - physical address of space reserved at the beginning
|
||
of the common buffer
|
||
|
||
Return Value:
|
||
|
||
NT Status code
|
||
|
||
--*/
|
||
{
|
||
ULONG allocLength;
|
||
PCHAR pageVirt;
|
||
PHYSICAL_ADDRESS pagePhys;
|
||
PPAGE_LIST_ENTRY pageList;
|
||
PHCD_TRANSFER_DESCRIPTOR td;
|
||
|
||
// Assert that sizeof(HCD_TRANSFER_DESCRIPTOR) is a power of 2
|
||
//
|
||
C_ASSERT((sizeof(HCD_TRANSFER_DESCRIPTOR) &
|
||
sizeof(HCD_TRANSFER_DESCRIPTOR) - 1) == 0);
|
||
|
||
// Round up ReserveLength to the next multiple of the
|
||
// sizeof(HCD_TRANSFER_DESCRIPTOR).
|
||
//
|
||
ReserveLength += sizeof(HCD_TRANSFER_DESCRIPTOR) - 1;
|
||
ReserveLength &= ~(sizeof(HCD_TRANSFER_DESCRIPTOR) - 1);
|
||
|
||
// Round up allocLength to the next multiple of PAGE_SIZE
|
||
//
|
||
allocLength = ReserveLength + PAGE_SIZE;
|
||
allocLength &= ~(PAGE_SIZE - 1);
|
||
|
||
// Now allocate the common buffer
|
||
//
|
||
pageVirt = HalAllocateCommonBuffer(DeviceData->AdapterObject,
|
||
allocLength,
|
||
&pagePhys,
|
||
CacheCommon);
|
||
|
||
if (pageVirt == NULL)
|
||
{
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
// Zero initialize the whole page
|
||
//
|
||
RtlZeroMemory(pageVirt, allocLength);
|
||
|
||
// Allocate a PAGE_LIST_ENTRY to track the page of commom buffer.
|
||
//
|
||
pageList = ExAllocatePoolWithTag(NonPagedPool,
|
||
sizeof(PAGE_LIST_ENTRY),
|
||
OpenHCI_TAG);
|
||
|
||
if (pageList == NULL)
|
||
{
|
||
HalFreeCommonBuffer(DeviceData->AdapterObject,
|
||
allocLength,
|
||
pagePhys,
|
||
pageVirt,
|
||
CacheCommon);
|
||
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
LOGENTRY(G, 'GROW', DeviceData, pageList, pagePhys.LowPart);
|
||
|
||
// Initialize the PAGE_LIST_ENTRY to track the page of commom buffer.
|
||
//
|
||
pageList->BufferSize = allocLength;
|
||
|
||
pageList->VirtAddr = pageVirt;
|
||
|
||
pageList->PhysAddr = pagePhys;
|
||
|
||
pageList->FirstTDVirt = (PHCD_TRANSFER_DESCRIPTOR)(pageVirt + ReserveLength);
|
||
|
||
pageList->LastTDVirt = (PHCD_TRANSFER_DESCRIPTOR)(pageVirt + allocLength) - 1;
|
||
|
||
pageList->FirstTDPhys = pagePhys;
|
||
pageList->FirstTDPhys.LowPart += ReserveLength;
|
||
|
||
pageList->LastTDPhys = pagePhys;
|
||
pageList->LastTDPhys.LowPart += allocLength - sizeof(PHCD_TRANSFER_DESCRIPTOR);;
|
||
|
||
|
||
// Add the PAGE_LIST_ENTRY to the device extension
|
||
//
|
||
ExInterlockedPushEntryList((PSINGLE_LIST_ENTRY)&DeviceData->PageList,
|
||
(PSINGLE_LIST_ENTRY)pageList,
|
||
&DeviceData->PageListSpin);
|
||
|
||
// Add all of the TDs in the page of common buffer to the
|
||
// Free Descriptor list.
|
||
//
|
||
pagePhys = pageList->FirstTDPhys;
|
||
|
||
for (td = pageList->FirstTDVirt; td <= pageList->LastTDVirt; td++)
|
||
{
|
||
// Initialize the TD PhysicalAddress field to point back to the TD
|
||
//
|
||
td->PhysicalAddress = pagePhys.LowPart;
|
||
|
||
pagePhys.LowPart += sizeof(HCD_TRANSFER_DESCRIPTOR);
|
||
|
||
ExInterlockedPushEntryList(&DeviceData->FreeDescriptorList,
|
||
(PSINGLE_LIST_ENTRY)td,
|
||
&DeviceData->DescriptorsSpin);
|
||
|
||
InterlockedIncrement(&DeviceData->FreeDescriptorCount);
|
||
}
|
||
|
||
// Return pointers to the reserved space if desired
|
||
//
|
||
if (VirtAddr)
|
||
{
|
||
*VirtAddr = pageList->VirtAddr;
|
||
}
|
||
if (PhysAddr)
|
||
{
|
||
*PhysAddr = pageList->PhysAddr;
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
OpenHCI_ReserveDescriptors(
|
||
IN PHCD_DEVICE_DATA DeviceData,
|
||
IN ULONG DescriptorCount
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Reserve Transfer/Endpoint Descriptors.
|
||
|
||
NOTE:
|
||
This routine is called only by openendpoint so it will never be
|
||
rentered.
|
||
|
||
Arguments:
|
||
|
||
DeviceData - pointer to a device extension
|
||
DescriptorCount - number of descriptors to reserve
|
||
|
||
Return Value:
|
||
|
||
NT Status code
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS ntStatus = STATUS_SUCCESS;
|
||
|
||
while (DeviceData->FreeDescriptorCount < DescriptorCount &&
|
||
NT_SUCCESS(ntStatus))
|
||
{
|
||
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_CALL_TRACE,
|
||
("'grow pool by one page\n"));
|
||
|
||
ntStatus = OpenHCI_GrowDescriptorPool(DeviceData,
|
||
0,
|
||
NULL,
|
||
NULL);
|
||
}
|
||
|
||
if (NT_SUCCESS(ntStatus))
|
||
{
|
||
DeviceData->FreeDescriptorCount -= DescriptorCount;
|
||
}
|
||
|
||
LOGENTRY(G, 'rsDS',
|
||
DescriptorCount, DeviceData->FreeDescriptorCount, ntStatus);
|
||
|
||
return ntStatus;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
OpenHCI_UnReserveDescriptors(
|
||
IN PHCD_DEVICE_DATA DeviceData,
|
||
IN ULONG DescriptorCount
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Free reserved Transfer/Endpoint Descriptors.
|
||
|
||
NOTE:
|
||
This routine is called only by closeendpoint so it will never be
|
||
rentered.
|
||
|
||
|
||
Arguments:
|
||
|
||
DeviceData - pointer to a device extension
|
||
DescriptorCount - number of descriptors to reserve
|
||
|
||
Return Value:
|
||
|
||
NT Status code
|
||
|
||
--*/
|
||
{
|
||
DeviceData->FreeDescriptorCount += DescriptorCount;
|
||
LOGENTRY(G, 'urDS', DescriptorCount, DeviceData->FreeDescriptorCount, 0);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
PHCD_TRANSFER_DESCRIPTOR
|
||
OpenHCI_Alloc_HcdTD(
|
||
PHCD_DEVICE_DATA DeviceData
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
allocate a reserved desriptor, this routine can be called at
|
||
IRQL >= DISPATCH_LEVEL
|
||
|
||
Arguments:
|
||
|
||
DeviceData - pointer to a device extension
|
||
|
||
Return Value:
|
||
|
||
NT Status code
|
||
|
||
--*/
|
||
{
|
||
PHCD_TRANSFER_DESCRIPTOR td;
|
||
|
||
td = (PHCD_TRANSFER_DESCRIPTOR)
|
||
ExInterlockedPopEntryList(&DeviceData->FreeDescriptorList,
|
||
&DeviceData->DescriptorsSpin);
|
||
|
||
LOGENTRY(G, 'alTD', &DeviceData->FreeDescriptorList, td, DeviceData->FreeDescriptorCount);
|
||
|
||
OHCI_ASSERT(td != NULL);
|
||
OHCI_ASSERT(td->Flags == 0);
|
||
|
||
OHCI_ASSERT((td->PhysicalAddress & (PAGE_SIZE-1)) ==
|
||
((ULONG_PTR)td & (PAGE_SIZE-1)));
|
||
|
||
// Initialize the TD NextTD pointer to a non-zero value before setting
|
||
// the TD_FLAG_INUSE flag. The only time a TD NextTD pointer should be
|
||
// zero when the TD_FLAG_INUSE flag is set is when the TD is the tail
|
||
// end of a done list.
|
||
//
|
||
td->HcTD.NextTD = 0x2BAD2BAD;
|
||
|
||
td->Flags = TD_FLAG_INUSE;
|
||
|
||
//
|
||
// if we fail to get a td we have a bug since we always reserve
|
||
// enough for the worst case scenario when an endpoint is opened
|
||
//
|
||
|
||
return td;
|
||
}
|
||
|
||
|
||
VOID
|
||
OpenHCI_Free_HcdTD(
|
||
PHCD_DEVICE_DATA DeviceData,
|
||
PHCD_TRANSFER_DESCRIPTOR Td
|
||
)
|
||
{
|
||
LOGENTRY(G, 'frTD', &DeviceData->FreeDescriptorList, Td, DeviceData->FreeDescriptorCount);
|
||
|
||
OHCI_ASSERT((Td->PhysicalAddress & (PAGE_SIZE-1)) ==
|
||
((ULONG_PTR)Td & (PAGE_SIZE-1)));
|
||
|
||
OHCI_ASSERT(Td->Flags == TD_FLAG_INUSE);
|
||
|
||
Td->Flags = 0;
|
||
|
||
ExInterlockedPushEntryList(&DeviceData->FreeDescriptorList,
|
||
(PSINGLE_LIST_ENTRY) Td,
|
||
&DeviceData->DescriptorsSpin);
|
||
}
|
||
|
||
|
||
PHCD_ENDPOINT_DESCRIPTOR
|
||
OpenHCI_Alloc_HcdED(
|
||
PHCD_DEVICE_DATA DeviceData
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
allocate a reserved desriptor, this routine can be called at
|
||
IRQL >= DISPATCH_LEVEL
|
||
|
||
Arguments:
|
||
|
||
DeviceData - pointer to a device extension
|
||
|
||
Return Value:
|
||
|
||
NT Status code
|
||
|
||
--*/
|
||
{
|
||
PHCD_ENDPOINT_DESCRIPTOR ed;
|
||
|
||
ed = (PHCD_ENDPOINT_DESCRIPTOR)
|
||
ExInterlockedPopEntryList(&DeviceData->FreeDescriptorList,
|
||
&DeviceData->DescriptorsSpin);
|
||
|
||
LOGENTRY(G, 'alED', &DeviceData->FreeDescriptorList, ed, DeviceData->FreeDescriptorCount);
|
||
|
||
OHCI_ASSERT(ed != NULL);
|
||
OHCI_ASSERT(ed->Flags == 0);
|
||
|
||
OHCI_ASSERT((ed->PhysicalAddress & (PAGE_SIZE-1)) ==
|
||
((ULONG_PTR)ed & (PAGE_SIZE-1)));
|
||
|
||
ed->Flags = TD_FLAG_INUSE | TD_FLAG_IS_ED;
|
||
|
||
//
|
||
// if we fail to get a td we have a bug since we always reserve
|
||
// enough for the worst case scenario when an endpoint is opened
|
||
//
|
||
|
||
return ed;
|
||
}
|
||
|
||
|
||
PHCD_TRANSFER_DESCRIPTOR
|
||
OpenHCI_LogDesc_to_PhyDesc(
|
||
PHCD_DEVICE_DATA DeviceData,
|
||
ULONG LogDesc
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
This routine scans the list of pages allocated by
|
||
HalAllocateCommonBuffer into the list DeviceData->PageList
|
||
for the storage of Descriptors.
|
||
The first entries describe the virtual and logical
|
||
addresses of those pages.
|
||
|
||
Arguments:
|
||
LogDesc is the logical address of a HcTD structure in memory.
|
||
|
||
Returned Value:
|
||
The virtual address of the HCD_Descriptor the (logical) HC_descriptor.
|
||
Note that the virtual address of the HC_TRANSFER_DESCRIPTOR
|
||
is the same as HCD_TRANSFER_DESCRIPTOR as well as
|
||
the HC_ENDPOINT_DESCRIPTOR the same as HCD_TRANSFER_DESCRIPTOR.
|
||
|
||
If no translation is found this will return NULL.
|
||
|
||
There are concurrent writes to DeviceData->PageList, but throughout
|
||
the life of DeviceData, only new pages will be pushed onto the list.
|
||
If these pages are added to the list while this function is running,
|
||
these pages cannot contain the logical address for which we are
|
||
searching.
|
||
--*/
|
||
{
|
||
PPAGE_LIST_ENTRY PageList;
|
||
PHCD_TRANSFER_DESCRIPTOR td;
|
||
|
||
if (LogDesc & (sizeof(HCD_TRANSFER_DESCRIPTOR)-1))
|
||
{
|
||
// The address is not properly aligned to be a TD.
|
||
// Something bad has happened. Try our best not to
|
||
// fault processing a bogus TD.
|
||
//
|
||
|
||
LOGENTRY(G, 'Phy1', DeviceData, LogDesc, 0);
|
||
|
||
//TEST_TRAP();
|
||
|
||
return NULL;
|
||
}
|
||
|
||
PageList = (PPAGE_LIST_ENTRY)DeviceData->PageList;
|
||
|
||
while (PageList)
|
||
{
|
||
if (LogDesc >= PageList->FirstTDPhys.LowPart &&
|
||
LogDesc <= PageList->LastTDPhys.LowPart)
|
||
{
|
||
td = (PHCD_TRANSFER_DESCRIPTOR)((PCHAR)PageList->FirstTDVirt +
|
||
LogDesc - PageList->FirstTDPhys.LowPart);
|
||
|
||
if (td->Flags == TD_FLAG_INUSE)
|
||
{
|
||
// Appears to be a valid TD
|
||
//
|
||
return td;
|
||
}
|
||
else
|
||
{
|
||
// The TD is not marked as an in-use TD. Something
|
||
// bad has happened. Try our best not to fault
|
||
// processing a bogus TD.
|
||
//
|
||
|
||
LOGENTRY(G, 'Phy2', DeviceData, LogDesc, 0);
|
||
|
||
//TEST_TRAP();
|
||
|
||
return NULL;
|
||
}
|
||
}
|
||
|
||
PageList = PageList->NextPage;
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
|
||
ULONG
|
||
OpenHCI_CheckBandwidth(
|
||
IN PHCD_DEVICE_DATA DeviceData,
|
||
IN UCHAR List,
|
||
OUT PCHAR BestList
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine scans all the scheduling lists of frequency
|
||
determined by the base List passed in and returns the worst
|
||
bandwidth found (i.e., max in use by any given scheduling
|
||
list) and the list which had the least bandwidth in use.
|
||
|
||
All lists of the appropriate frequency are checked
|
||
|
||
Arguments:
|
||
|
||
DeviceData - pointer to this controller's data area
|
||
|
||
List - must be a base scheduling list.
|
||
I.e., it must be one of ED_INTERRUPT_1ms, ED_INTERRUPT_2ms,
|
||
ED_INTERRUPT_4ms, ..., ED_INTERRUPT_32ms.
|
||
|
||
BestList - Pointer to a ULONG that recieves the list number of the
|
||
list with least bandwidth in use.
|
||
|
||
Returned Value:
|
||
|
||
The maximum bandwidth in use by any of the selected lists.
|
||
|
||
--*/
|
||
{
|
||
ULONG LastList, Index;
|
||
ULONG BestBandwidth, WorstBandwidth, Bandwidth;
|
||
|
||
WorstBandwidth = 0;
|
||
BestBandwidth = ~(ULONG) 0;
|
||
|
||
for (LastList = List + List; List <= LastList; List++) {
|
||
|
||
//
|
||
// Sum bandwidth in use in this scheduling time
|
||
//
|
||
Bandwidth = 0;
|
||
for (Index = List;
|
||
Index != ED_EOF;
|
||
Index = DeviceData->EDList[Index].Next) {
|
||
Bandwidth += DeviceData->EDList[Index].Bandwidth;
|
||
}
|
||
|
||
//
|
||
// Remember best and worst
|
||
//
|
||
if (Bandwidth < BestBandwidth) {
|
||
BestBandwidth = Bandwidth;
|
||
if (BestList != NULL) {
|
||
*BestList = List;
|
||
}
|
||
}
|
||
if (Bandwidth > WorstBandwidth) {
|
||
WorstBandwidth = Bandwidth;
|
||
}
|
||
}
|
||
|
||
LOGENTRY(G, 'ckBW', Bandwidth, 0, WorstBandwidth);
|
||
|
||
return WorstBandwidth;
|
||
}
|
||
|
||
|
||
PHCD_ENDPOINT_DESCRIPTOR
|
||
InsertEDForEndpoint(
|
||
IN PHCD_DEVICE_DATA DeviceData,
|
||
IN PHCD_ENDPOINT Endpoint,
|
||
IN UCHAR ListIndex,
|
||
IN PHCD_TRANSFER_DESCRIPTOR *TailTd
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Insert an endpoint into the h/w schedule, optionally this will
|
||
allocate an endpoint descriptor and/or a dummy transfer descriptor.
|
||
|
||
Arguments:
|
||
|
||
Endpoint - pointer to the endpoint to be included in schedule
|
||
if this parameter is NULL then we are inserting a dummy ED
|
||
that has no associted endpoint.
|
||
|
||
|
||
--*/
|
||
{
|
||
PHCD_ED_LIST list;
|
||
PHCD_ENDPOINT_DESCRIPTOR tailED, ed;
|
||
PHCD_TRANSFER_DESCRIPTOR td;
|
||
KIRQL oldIrql;
|
||
UCHAR endpointType;
|
||
|
||
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_CALL_TRACE,
|
||
("'InsertEDForEndpoint %x\n", Endpoint));
|
||
|
||
list = &DeviceData->EDList[ListIndex];
|
||
|
||
LOGENTRY(G, 'inED', Endpoint, ListIndex, list);
|
||
|
||
if (Endpoint == NULL) {
|
||
// this is a dummy ED
|
||
td = NULL;
|
||
ed = NULL;
|
||
} else {
|
||
// we have an endpoint
|
||
|
||
td = Endpoint->HcdHeadP;
|
||
ed = Endpoint->HcdED;
|
||
}
|
||
|
||
if (td == NULL) {
|
||
//
|
||
// no TD,
|
||
// get a dummy TD and attach to the endpoint
|
||
//
|
||
if (td = OpenHCI_Alloc_HcdTD(DeviceData)) {
|
||
|
||
td->UsbdRequest = TD_NOREQUEST_SIG;
|
||
// if an endpoint is specified then we want this TD to be
|
||
// the dummy TD that the haed & tail point to for a real
|
||
// endpoint
|
||
if (Endpoint != NULL) {
|
||
Endpoint->HcdHeadP = Endpoint->HcdTailP = td;
|
||
}
|
||
|
||
// return the tail td if asked
|
||
if (TailTd) {
|
||
*TailTd = td;
|
||
}
|
||
}
|
||
LOGENTRY(G, 'dyTD', Endpoint, td, list);
|
||
}
|
||
|
||
|
||
if (ed == NULL) {
|
||
//
|
||
// Need an ED,
|
||
// Get an ED, attach it to endpoint if we have one
|
||
//
|
||
ed = OpenHCI_Alloc_HcdED(DeviceData);
|
||
|
||
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_CALL_TRACE,
|
||
("'InsertEDForEndpoint ED = %x\n", ed));
|
||
|
||
if (Endpoint && ed) {
|
||
Endpoint->HcdED = ed;
|
||
}
|
||
|
||
LOGENTRY(G, 'dyED', Endpoint, ed, 0);
|
||
}
|
||
|
||
OHCI_ASSERT(td != 0);
|
||
OHCI_ASSERT(ed != 0);
|
||
|
||
if (ed == NULL || td == NULL) {
|
||
// we are probably screwed
|
||
OpenHCI_KdTrap(("'failed to alloc dummy TD/ED\n"));
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// Initialize an endpoint descriptor for this endpoint
|
||
//
|
||
|
||
if (Endpoint != NULL) {
|
||
//
|
||
// This is for a real endpoint
|
||
//
|
||
ed->HcED.Control = Endpoint->Control;
|
||
endpointType = Endpoint->Type;
|
||
} else {
|
||
//
|
||
// no real endpoint exists, this is a dummy ED
|
||
// used to work around a hardware bug, we set the skip
|
||
// bit so the HC does not process it.
|
||
//
|
||
ed->HcED.Control = HcEDControl_SKIP;
|
||
if (ListIndex == ED_BULK) {
|
||
endpointType = USB_ENDPOINT_TYPE_BULK;
|
||
} else if (ListIndex == ED_CONTROL) {
|
||
endpointType = USB_ENDPOINT_TYPE_CONTROL;
|
||
} else if (ListIndex == ED_ISOCHRONOUS) {
|
||
endpointType = USB_ENDPOINT_TYPE_ISOCHRONOUS;
|
||
} else {
|
||
endpointType = USB_ENDPOINT_TYPE_INTERRUPT;
|
||
}
|
||
}
|
||
|
||
ed->Endpoint = Endpoint;
|
||
ed->ListIndex = ListIndex;
|
||
ed->PauseFlag = HCD_ED_PAUSE_NOT_PAUSED;
|
||
ed->HcED.TailP = ed->HcED.HeadP = td->PhysicalAddress;
|
||
|
||
KeAcquireSpinLock(&DeviceData->EDListSpin, &oldIrql);
|
||
|
||
//
|
||
// Link endpoint descriptor into HCD tracking queue
|
||
//
|
||
|
||
if (endpointType != USB_ENDPOINT_TYPE_ISOCHRONOUS ||
|
||
IsListEmpty(&list->Head)) {
|
||
|
||
//
|
||
// if it is not an iso ED or there are no EDs in the list
|
||
// then link it to the head of the hw queue
|
||
//
|
||
|
||
InsertHeadList(&list->Head, &ed->Link);
|
||
if (list->HcRegister) {
|
||
// update the hardware register that points to the list head
|
||
|
||
LOGENTRY(G, 'INH1', list, ed, list->PhysicalHead);
|
||
// tail points to old head
|
||
ed->HcED.NextED = READ_REGISTER_ULONG(list->PhysicalHead);
|
||
// new head is this ed
|
||
WRITE_REGISTER_ULONG(list->PhysicalHead, ed->PhysicalAddress);
|
||
} else {
|
||
LOGENTRY(G, 'INH2', list, ed, list->PhysicalHead);
|
||
// tail points to old head
|
||
ed->HcED.NextED = *list->PhysicalHead;
|
||
// new head is this ed
|
||
*list->PhysicalHead = ed->PhysicalAddress;
|
||
}
|
||
} else {
|
||
//
|
||
// Something already on the list,
|
||
// Link ED into tail of ED list
|
||
//
|
||
|
||
|
||
tailED = CONTAINING_RECORD(list->Head.Blink,
|
||
HCD_ENDPOINT_DESCRIPTOR,
|
||
Link);
|
||
|
||
LOGENTRY(G, 'INT1', list, ed, 0);
|
||
InsertTailList(&list->Head, &ed->Link);
|
||
ed->HcED.NextED = 0;
|
||
tailED->HcED.NextED = ed->PhysicalAddress;
|
||
}
|
||
|
||
KeReleaseSpinLock(&DeviceData->EDListSpin, oldIrql);
|
||
|
||
return (ed);
|
||
}
|
||
|
||
|
||
VOID
|
||
RemoveEDForEndpoint(
|
||
IN PHCD_ENDPOINT Endpoint
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
Remove an endpoint from the hardware schedule.
|
||
|
||
Arguments:
|
||
Endpouint - the endpoint assocaited with the ED to remove.
|
||
|
||
|
||
An Endpoint pointing to the Endpoint Descriptor to be removed.
|
||
if breakEDLink then the link between the Endpoint Descriptor and its
|
||
corresponding endpoint is severed.
|
||
|
||
If this link is severed then the Endpoint Descriptor will be returned
|
||
to the descriptor free pool during the next interrupt.
|
||
--*/
|
||
{
|
||
PHCD_DEVICE_DATA DeviceData;
|
||
PHCD_ED_LIST list;
|
||
PHCD_ENDPOINT_DESCRIPTOR previousED, ed = Endpoint->HcdED;
|
||
ULONG listDisable;
|
||
KIRQL oldIrql;
|
||
UCHAR tmp;
|
||
PHC_OPERATIONAL_REGISTER HC;
|
||
|
||
ASSERT_ENDPOINT(Endpoint);
|
||
DeviceData = Endpoint->DeviceData;
|
||
list = &DeviceData->EDList[Endpoint->ListIndex];
|
||
|
||
LOGENTRY(G, 'RMed', Endpoint, ed, list);
|
||
|
||
// if we are in the active list we need to remove ourselves
|
||
KeAcquireSpinLock(&DeviceData->HcDmaSpin, &oldIrql);
|
||
if (Endpoint->EpFlags & EP_IN_ACTIVE_LIST) {
|
||
RemoveEntryList(&Endpoint->EndpointListEntry);
|
||
CLR_EPFLAG(Endpoint, EP_IN_ACTIVE_LIST);
|
||
}
|
||
KeReleaseSpinLock(&DeviceData->HcDmaSpin, oldIrql);
|
||
|
||
/* Prevent the Host Controller from processing this ED */
|
||
ed->HcED.sKip = TRUE;
|
||
|
||
KeAcquireSpinLock(&DeviceData->EDListSpin, &oldIrql);
|
||
|
||
{
|
||
/* Unlink the ED from the physical ED list */
|
||
LOGENTRY(G, 'ULed', Endpoint, ed, list);
|
||
if (&list->Head == ed->Link.Blink) {
|
||
if (list->HcRegister) {
|
||
WRITE_REGISTER_ULONG(list->PhysicalHead, ed->HcED.NextED);
|
||
} else {
|
||
*list->PhysicalHead = ed->HcED.NextED;
|
||
}
|
||
previousED = NULL;
|
||
} else {
|
||
previousED =
|
||
CONTAINING_RECORD(ed->Link.Blink,
|
||
HCD_ENDPOINT_DESCRIPTOR,
|
||
Link);
|
||
previousED->HcED.NextED = ed->HcED.NextED;
|
||
}
|
||
|
||
/* Unlink the ED from HCD list */
|
||
RemoveEntryList(&ed->Link);
|
||
ed->ListIndex = ED_EOF;
|
||
}
|
||
KeReleaseSpinLock(&DeviceData->EDListSpin, oldIrql);
|
||
|
||
/* Break the Endpoint / ED connection. This descriptor is now heading for
|
||
* the slaughter. */
|
||
Endpoint->HcdED = NULL;
|
||
ed->Endpoint = NULL;
|
||
|
||
|
||
OHCI_ASSERT(Endpoint->HcdHeadP == Endpoint->HcdTailP);
|
||
OHCI_ASSERT((ed->HcED.HeadP & ~15) == ed->HcED.TailP);
|
||
/* AKA there are no transfers on this queue. The HC will not touch any of
|
||
* these TD's. We can free the 'sentinel' TD here, but we CANNOT actually
|
||
* zero out the head and tail pointers, because the HC could still be
|
||
* looking at this descriptor. Later, in the irq, when we take this
|
||
* endpoint off the reclamation list, we much free the ED, and ignore the
|
||
* non zero values of HeadP and TailP. */
|
||
OpenHCI_Free_HcdTD(DeviceData, Endpoint->HcdHeadP);
|
||
|
||
/*
|
||
* The control and bulk lists are round robbin. Therefore we need to
|
||
* disable these lists to insure that the 'current ED' pointer is not
|
||
* pointing to that which we are removing.
|
||
*/
|
||
switch (Endpoint->ListIndex) {
|
||
case ED_CONTROL:
|
||
listDisable = ~(ULONG) HcCtrl_ControlListEnable;
|
||
break;
|
||
case ED_BULK:
|
||
listDisable = ~(ULONG) HcCtrl_BulkListEnable;
|
||
break;
|
||
default:
|
||
list->Bandwidth -= Endpoint->Bandwidth;
|
||
DeviceData->MaxBandwidthInUse
|
||
= OpenHCI_CheckBandwidth(DeviceData,
|
||
ED_INTERRUPT_32ms,
|
||
&tmp);
|
||
listDisable = 0;
|
||
}
|
||
|
||
HC = DeviceData->HC;
|
||
WRITE_REGISTER_ULONG(&HC->HcInterruptStatus, HcInt_StartOfFrame);
|
||
/* Clear the SOF interrupt pending status */
|
||
|
||
{
|
||
if (listDisable) {
|
||
KeSynch_HcControl context;
|
||
context.NewHcControl.ul = listDisable;
|
||
context.DeviceData = DeviceData;
|
||
KeSynchronizeExecution(DeviceData->InterruptObject,
|
||
OpenHCI_HcControl_AND,
|
||
&context);
|
||
|
||
KeAcquireSpinLock(&DeviceData->ReclamationSpin, &oldIrql);
|
||
InsertTailList(&DeviceData->StalledEDReclamation, &ed->Link);
|
||
} else {
|
||
KeAcquireSpinLock(&DeviceData->ReclamationSpin, &oldIrql);
|
||
InsertTailList(&DeviceData->RunningEDReclamation, &ed->Link);
|
||
}
|
||
|
||
ed->ReclamationFrame = Get32BitFrameNumber(DeviceData) + 1;
|
||
WRITE_REGISTER_ULONG(&HC->HcInterruptEnable, HcInt_StartOfFrame);
|
||
}
|
||
KeReleaseSpinLock(&DeviceData->ReclamationSpin, oldIrql);
|
||
|
||
// free our descriptors and endpoint
|
||
OpenHCI_UnReserveDescriptors(DeviceData, Endpoint->DescriptorsReserved);
|
||
|
||
// now free the endpoint
|
||
|
||
Endpoint->Sig = 0;
|
||
ExFreePool(Endpoint);
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
OpenHCI_OpenEndpoint(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PHCD_DEVICE_DATA DeviceData,
|
||
IN OUT PHCD_URB urb
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Create a OpenHCI endpoint, this function is called from
|
||
OpenHCI_URB_Dispatch to create a new endpoint structure.
|
||
|
||
There are three things that can cause this routine to fail:
|
||
1) ExAllocatePool may fail to allocate an HCD_ENDPOINT,
|
||
2) OpenHCI_ReserveDescriptors may not be able to find enough
|
||
descriptors, and
|
||
3) OpenHCI_ReserveBandwidth may not be able to find bandwidth
|
||
in the schedule.
|
||
|
||
This routine is simplified because USBD serializes OpenEndpoint
|
||
and CloseEndpoint URBs
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - pointer to a device object
|
||
|
||
Irp - pointer to an I/O Request Packet
|
||
|
||
Return Value:
|
||
|
||
NT Status code
|
||
|
||
|
||
--*/
|
||
{
|
||
PHCD_ENDPOINT endpoint;
|
||
PUSB_ENDPOINT_DESCRIPTOR endpointDescriptor;
|
||
UCHAR WhichList;
|
||
NTSTATUS ntStatus = STATUS_SUCCESS;
|
||
ULONG tdsNeeded;
|
||
|
||
OpenHCI_KdPrintDD(DeviceData,
|
||
OHCI_DBG_END_TRACE, ("'enter OpenEndpoint\n"));
|
||
|
||
#if DBG
|
||
/*
|
||
* We are assuming that the caller of _OpenEndpoint and _Close endpoint
|
||
* make only serial calls. For this reason we will protect ourselves. If
|
||
* this is violated we return STATUS_DEVICE_BUSY.
|
||
*/
|
||
|
||
if (0 < InterlockedIncrement(&DeviceData->OpenCloseSync)) {
|
||
OpenHCI_KdTrap(("'ohciurb.c: _OpenEndpoint: Non serial call! %d",
|
||
DeviceData->OpenCloseSync));
|
||
TEST_TRAP();
|
||
return STATUS_DEVICE_BUSY;
|
||
}
|
||
#endif // DBG
|
||
|
||
//
|
||
// make sure the length of the urb is what we expect
|
||
//
|
||
|
||
if (urb->HcdUrbOpenEndpoint.Length !=
|
||
sizeof(struct _URB_HCD_OPEN_ENDPOINT)) {
|
||
urb->UrbHeader.Status = USBD_STATUS_INVALID_PARAMETER;
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
urb->UrbHeader.Status = USBD_STATUS_SUCCESS;
|
||
|
||
//
|
||
// information about the endpoint comes from the USB endpoint
|
||
// descriptor passed in the URB.
|
||
//
|
||
|
||
endpointDescriptor = urb->HcdUrbOpenEndpoint.EndpointDescriptor;
|
||
urb->HcdUrbOpenEndpoint.HcdEndpoint = NULL;
|
||
|
||
// How does one transmit to an endpoint that does not have
|
||
// a positive max packet size?
|
||
// A: this is an error
|
||
|
||
if (endpointDescriptor->wMaxPacketSize == 0) {
|
||
endpoint =
|
||
ZERO_LOAD_ENDPOINT(DeviceData);
|
||
|
||
urb->UrbHeader.Status = USBD_STATUS_SUCCESS;
|
||
ntStatus = STATUS_SUCCESS;
|
||
goto OpenHCI_OpenEndpoint_Done;
|
||
}
|
||
|
||
//
|
||
// Allocate resources for the endpoint, this includes an endpoint
|
||
// handle that will be passed to us in subsequent transfer requests
|
||
//
|
||
|
||
endpoint = (PHCD_ENDPOINT)
|
||
ExAllocatePoolWithTag(NonPagedPool,
|
||
sizeof(HCD_ENDPOINT),
|
||
OpenHCI_TAG);
|
||
|
||
if (endpoint == NULL) {
|
||
urb->UrbHeader.Status = USBD_STATUS_NO_MEMORY;
|
||
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto OpenHCI_OpenEndpoint_Done;
|
||
}
|
||
//
|
||
// Start initializing the endpoint structure
|
||
//
|
||
InitializeListHead(&endpoint->RequestQueue);
|
||
endpoint->Sig = SIG_EP;
|
||
endpoint->EpFlags = 0;
|
||
endpoint->LowSpeed = FALSE;
|
||
endpoint->Isochronous = FALSE;
|
||
endpoint->Rate = endpointDescriptor->bInterval;
|
||
|
||
// Initial conditon:
|
||
// No active transfers
|
||
// Max of two active transfers
|
||
//
|
||
endpoint->EndpointStatus = 0;
|
||
endpoint->MaxRequest = 2;
|
||
|
||
endpoint->AbortIrp = NULL;
|
||
|
||
endpoint->Type =
|
||
endpointDescriptor->bmAttributes & USB_ENDPOINT_TYPE_MASK;
|
||
|
||
if (DeviceData->HcFlags & HC_FLAG_SLOW_BULK_ENABLE &&
|
||
endpoint->Type == USB_ENDPOINT_TYPE_BULK) {
|
||
endpoint->MaxRequest = 1; // limit to only one transfer
|
||
SET_EPFLAG(endpoint, EP_ONE_TD);
|
||
}
|
||
|
||
endpoint->ListIndex = ED_EOF; // not on a list yet
|
||
endpoint->Bandwidth = 0; // assume bulk or control
|
||
endpoint->DeviceData = DeviceData;
|
||
endpoint->Control = 0;
|
||
endpoint->HcdED = NULL;
|
||
endpoint->HcdHeadP = endpoint->HcdTailP = NULL;
|
||
endpoint->DescriptorsReserved = 0;
|
||
endpoint->TrueTail = NULL;
|
||
KeInitializeSpinLock(&endpoint->QueueLock);
|
||
|
||
SET_EPFLAG(endpoint, EP_VIRGIN);
|
||
endpoint->FunctionAddress = urb->HcdUrbOpenEndpoint.DeviceAddress;
|
||
endpoint->EndpointNumber = endpointDescriptor->bEndpointAddress;
|
||
|
||
if (endpoint->Type == USB_ENDPOINT_TYPE_CONTROL) {
|
||
endpoint->Direction = HcEDDirection_Defer;
|
||
} else if (endpointDescriptor->bEndpointAddress & 0x80) {
|
||
endpoint->Direction = HcEDDirection_In;
|
||
} else {
|
||
endpoint->Direction = HcEDDirection_Out;
|
||
}
|
||
|
||
if (urb->HcdUrbOpenEndpoint.HcdEndpointFlags & USBD_EP_FLAG_LOWSPEED) {
|
||
endpoint->LowSpeed = TRUE;
|
||
}
|
||
|
||
if (endpoint->Type == USB_ENDPOINT_TYPE_ISOCHRONOUS) {
|
||
endpoint->Isochronous = TRUE;
|
||
}
|
||
endpoint->MaxPacket = endpointDescriptor->wMaxPacketSize;
|
||
//
|
||
// Figure out how many descriptors we need to reserve
|
||
//
|
||
endpoint->MaxTransfer = urb->HcdUrbOpenEndpoint.MaxTransferSize;
|
||
if (endpoint->FunctionAddress != DeviceData->RootHubAddress) {
|
||
//
|
||
// This is an ordinary device endpoint...
|
||
//
|
||
|
||
// the goal here is to reserve enough TDs so that we can
|
||
// program the largest transfer to the hardware
|
||
//
|
||
if (endpoint->Type == USB_ENDPOINT_TYPE_ISOCHRONOUS)
|
||
{
|
||
ULONG packetSize;
|
||
ULONG maxTransfer;
|
||
ULONG msPerTransfer;
|
||
ULONG packetsPerTD;
|
||
|
||
maxTransfer = endpoint->MaxTransfer;
|
||
packetSize = endpoint->MaxPacket;
|
||
|
||
// Assume that the client driver sized the MaxTransferSize by
|
||
// multiplying the MaxPacketSize by the maximum number of frames
|
||
// the client driver would submit in a single request, so compute
|
||
// the maximun number of frames per request by dividing the
|
||
// MaxTransferSize by the MaxPacketSize.
|
||
//
|
||
msPerTransfer = maxTransfer / packetSize;
|
||
|
||
// Isoch transfers are limited to 255 packets per transfer.
|
||
//
|
||
if (msPerTransfer > 255)
|
||
{
|
||
msPerTransfer = 255;
|
||
}
|
||
|
||
// A TD can only span one page crossing. In the worst cast we
|
||
// can only fit a page worth of packets into a single TD to
|
||
// guarantee that there is only one page crossing.
|
||
//
|
||
// packetSize packetsPerTD (worst case)
|
||
// 1 - 512 8
|
||
// 513 - 585 7
|
||
// 586 - 682 6
|
||
// 683 - 819 5
|
||
// 820 - 1023 4
|
||
//
|
||
packetsPerTD = OHCI_PAGE_SIZE / packetSize;
|
||
|
||
// Regardless of the MaxPacketSize, a single TD is limited to
|
||
// eight packets.
|
||
//
|
||
if (packetsPerTD > 8)
|
||
{
|
||
packetsPerTD = 8;
|
||
}
|
||
|
||
// Calculate the number of TDs needed by dividing the maximum
|
||
// number of frames per transfer (rounded up) by the worst case
|
||
// miminum number of frames that will fit in a TD
|
||
//
|
||
tdsNeeded = (msPerTransfer + packetsPerTD - 1) / packetsPerTD;
|
||
|
||
LOGENTRY(G, 'rISO', tdsNeeded, packetSize, maxTransfer);
|
||
|
||
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_END_TRACE,
|
||
("'reserve desc for iso ep (%d) pct siz = %d, max xfer = %d\n",
|
||
tdsNeeded, packetSize, maxTransfer));
|
||
|
||
// If the magic endpoint flag is set bump the MaxRequests
|
||
// all the way up to 32.
|
||
//
|
||
if (urb->HcdUrbOpenEndpoint.HcdEndpointFlags &
|
||
USBD_EP_FLAG_MAP_ADD_IO)
|
||
{
|
||
endpoint->MaxRequest = 32;
|
||
}
|
||
|
||
} else {
|
||
// we will need a TD for each page of data to transfer,
|
||
// worst case
|
||
tdsNeeded = (endpoint->MaxTransfer / (OHCI_PAGE_SIZE+1)) + 1;
|
||
|
||
// we need an xtra TD for the setup/status packet
|
||
if (endpoint->Type == USB_ENDPOINT_TYPE_CONTROL) {
|
||
tdsNeeded+=2;
|
||
}
|
||
}
|
||
|
||
// We need enough TDs to queue MaxRequest transfers plus:
|
||
// two EDs (why two EDs instead of one?), a dummy TD for the
|
||
// tail TD, and a spare (why?)
|
||
//
|
||
tdsNeeded = (tdsNeeded * endpoint->MaxRequest) + 4;
|
||
|
||
//
|
||
// pre-allocate the number of descriptors we will need
|
||
// for this endpoint
|
||
//
|
||
ntStatus = OpenHCI_ReserveDescriptors(DeviceData, tdsNeeded);
|
||
LOGENTRY(G, 'rTDS', endpoint, tdsNeeded, 0);
|
||
|
||
if (!NT_SUCCESS(ntStatus)) {
|
||
TEST_TRAP();
|
||
urb->UrbHeader.Status = USBD_STATUS_NO_MEMORY;
|
||
goto OpenHCI_OpenEndpoint_Done;
|
||
}
|
||
endpoint->DescriptorsReserved = tdsNeeded;
|
||
|
||
endpoint->Bandwidth =
|
||
(USHORT) USBD_CalculateUsbBandwidth(endpoint->MaxPacket,
|
||
endpoint->Type,
|
||
(BOOLEAN)endpoint->LowSpeed);
|
||
|
||
LOGENTRY(G, 'ndBW', endpoint, endpoint->Bandwidth, 0);
|
||
|
||
switch(endpoint->Type) {
|
||
case USB_ENDPOINT_TYPE_ISOCHRONOUS:
|
||
WhichList = ED_ISOCHRONOUS;
|
||
|
||
DeviceData->EDList[ED_ISOCHRONOUS].Bandwidth +=
|
||
endpoint->Bandwidth;
|
||
DeviceData->MaxBandwidthInUse += endpoint->Bandwidth;
|
||
break;
|
||
case USB_ENDPOINT_TYPE_INTERRUPT:
|
||
//
|
||
// Determine the scheduling period.
|
||
//
|
||
WhichList = ED_INTERRUPT_32ms;
|
||
while (WhichList >= endpoint->Rate && (WhichList >>= 1)) {
|
||
//
|
||
// Keep decrementing the schedule list until, the list
|
||
// chosen
|
||
// meets or exceeds the rate required by the endpoint.
|
||
//
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Determine which scheduling list has the least bandwidth
|
||
//
|
||
OpenHCI_CheckBandwidth(DeviceData, WhichList, &WhichList);
|
||
DeviceData->EDList[WhichList].Bandwidth += endpoint->Bandwidth;
|
||
|
||
//
|
||
// Recalculate the max bandwidth which is in use. This
|
||
// allows 1ms (isoc) pipe opens to occur with few calculation
|
||
//
|
||
DeviceData->MaxBandwidthInUse =
|
||
OpenHCI_CheckBandwidth(DeviceData,
|
||
ED_INTERRUPT_32ms,
|
||
NULL);
|
||
break;
|
||
case USB_ENDPOINT_TYPE_BULK:
|
||
WhichList = ED_BULK;
|
||
break;
|
||
case USB_ENDPOINT_TYPE_CONTROL:
|
||
WhichList = ED_CONTROL;
|
||
break;
|
||
}
|
||
|
||
endpoint->ListIndex = WhichList;
|
||
urb->HcdUrbOpenEndpoint.ScheduleOffset = WhichList;
|
||
|
||
//
|
||
// Verify the new max has not overcomitted the USB
|
||
//
|
||
LOGENTRY(G, 'vrBW', endpoint, DeviceData->MaxBandwidthInUse,
|
||
DeviceData->AvailableBandwidth);
|
||
|
||
if (DeviceData->MaxBandwidthInUse > DeviceData->AvailableBandwidth) {
|
||
//
|
||
// Too much, back this bandwidth out and fail the request
|
||
//
|
||
DeviceData->EDList[WhichList].Bandwidth -= endpoint->Bandwidth;
|
||
DeviceData->MaxBandwidthInUse =
|
||
OpenHCI_CheckBandwidth(DeviceData,
|
||
ED_INTERRUPT_32ms,
|
||
NULL);
|
||
|
||
//
|
||
// return a CAN_NOT_COMMIT_BANDWIDTH error.
|
||
urb->UrbHeader.Status = USBD_STATUS_NO_BANDWIDTH;
|
||
ntStatus = STATUS_UNSUCCESSFUL;
|
||
|
||
goto OpenHCI_OpenEndpoint_Done;
|
||
}
|
||
|
||
//
|
||
// Add to Host Controller schedule processing
|
||
//
|
||
|
||
// if lowspeed control and Hs/ls hack enabled
|
||
// put control transfers on the periodic list
|
||
|
||
if (endpoint->LowSpeed &&
|
||
DeviceData->HcFlags & HC_FLAG_USE_HYDRA_HACK &&
|
||
endpoint->ListIndex == ED_CONTROL) {
|
||
|
||
OpenHCI_KdPrint((1, "'*** do hs/ls fix for control ep\n"));
|
||
// put control on the interrupt list
|
||
endpoint->ListIndex = ED_INTERRUPT_1ms;
|
||
|
||
}
|
||
|
||
InsertEDForEndpoint(endpoint->DeviceData,
|
||
endpoint,
|
||
endpoint->ListIndex,
|
||
NULL);
|
||
|
||
OpenHCI_KdPrintDD(DeviceData,
|
||
OHCI_DBG_END_TRACE, ("'Open Endpoint:\n"));
|
||
|
||
} else {
|
||
//
|
||
// note that if the controller has lost power
|
||
// before the RH is started we need to restore the HC state
|
||
// before initializing the root hub.
|
||
//
|
||
|
||
if (DeviceData->HcFlags & HC_FLAG_LOST_POWER) {
|
||
BOOLEAN lostPower;
|
||
PHC_OPERATIONAL_REGISTER HC;
|
||
|
||
HC = DeviceData->HC;
|
||
|
||
// should only get here in the case where the
|
||
// HC looses power
|
||
//
|
||
OpenHCI_RestoreHCstate(DeviceData, &lostPower);
|
||
|
||
OHCI_ASSERT(lostPower == TRUE);
|
||
|
||
OpenHCI_Resume(DeviceObject, lostPower);
|
||
|
||
KeSetTimerEx(&DeviceData->DeadmanTimer,
|
||
RtlConvertLongToLargeInteger(-10000),
|
||
10,
|
||
&DeviceData->DeadmanTimerDPC);
|
||
}
|
||
|
||
//
|
||
// This is an emulated Root Hub endpoint
|
||
//
|
||
SET_EPFLAG(endpoint, EP_ROOT_HUB);
|
||
|
||
if (endpoint->EndpointNumber == 1 &&
|
||
endpoint->Type == USB_ENDPOINT_TYPE_INTERRUPT) {
|
||
DeviceData->RootHubInterrupt = endpoint;
|
||
/*
|
||
* Note: if you open up two endpoints to the Interrupt endpoint
|
||
* of the root hub then the first one stops responding. But this
|
||
* is only fare as you should not open up two pipes to the same
|
||
* device and endpoint number.
|
||
*/
|
||
} else {
|
||
DeviceData->RootHubControl = endpoint;
|
||
}
|
||
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_END_INFO,
|
||
("'Open Endp (Root Hub Emulation)\n"));
|
||
}
|
||
|
||
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_END_INFO,
|
||
("'ED: Type %d, Dir %d Addr %d FUNC Address %d LowSpeed %d\n",
|
||
endpoint->Type,
|
||
endpoint->Direction,
|
||
endpoint->EndpointNumber,
|
||
endpoint->FunctionAddress,
|
||
endpoint->LowSpeed));
|
||
|
||
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_END_INFO,
|
||
("' MaxPacket %x Interval Requested %d List Selected %d\n",
|
||
endpoint->MaxPacket,
|
||
endpointDescriptor->bInterval,
|
||
endpoint->ListIndex));
|
||
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_END_INFO,
|
||
("'*****HCD_ENDPOINT addr: 0x%08x HcdED: 0x%08x\n\n\n",
|
||
endpoint, endpoint->HcdED));
|
||
|
||
OpenHCI_OpenEndpoint_Done:
|
||
|
||
//
|
||
// Complete the IRP, status is in the status field of the URB
|
||
//
|
||
|
||
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_END_TRACE,
|
||
("'exit OpenHCI_OpenEndpoint (URB STATUS = %x)\n",
|
||
urb->UrbHeader.Status));
|
||
|
||
if (NT_SUCCESS(ntStatus)) {
|
||
urb->HcdUrbOpenEndpoint.HcdEndpoint = endpoint;
|
||
} else {
|
||
ASSERT(endpoint != ZERO_LOAD_ENDPOINT(DeviceData));
|
||
if (endpoint) {
|
||
OpenHCI_UnReserveDescriptors(DeviceData,
|
||
endpoint->DescriptorsReserved);
|
||
OHCI_ASSERT(!(endpoint->EpFlags & EP_IN_ACTIVE_LIST));
|
||
ExFreePool(endpoint);
|
||
}
|
||
}
|
||
|
||
#if DBG
|
||
InterlockedDecrement(&DeviceData->OpenCloseSync);
|
||
#endif //DBG
|
||
LOGENTRY(G, 'opEP', endpoint, urb, ntStatus);
|
||
|
||
return ntStatus;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
OpenHCI_CloseEndpoint(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PHCD_DEVICE_DATA DeviceData,
|
||
IN OUT PHCD_URB urb
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Free a OpenHCI endpoint, if there are any pending transfers for this
|
||
endpoint this routine should fail.
|
||
|
||
Arguments:
|
||
DeviceObject - pointer to a device object
|
||
Irp - pointer to an I/O Request Packet
|
||
|
||
Return Value:
|
||
If out of synchronous call with close and open
|
||
then return STATUS_DEVICE_BUSY otherwise
|
||
return STATUS_SUCCESS
|
||
|
||
--*/
|
||
{
|
||
PHCD_ENDPOINT endpoint = urb->HcdUrbCloseEndpoint.HcdEndpoint;
|
||
PHCD_ENDPOINT_DESCRIPTOR ed;
|
||
BOOLEAN outstandingTransfers, activeTransfers;
|
||
KIRQL irql;
|
||
NTSTATUS ntStatus = STATUS_SUCCESS;
|
||
BOOLEAN freeEP = TRUE;
|
||
// LARGE_INTEGER time;
|
||
|
||
OpenHCI_KdPrintDD(DeviceData,
|
||
OHCI_DBG_END_TRACE, ("'enter CloseEndpoint\n"));
|
||
|
||
LOGENTRY(G, 'rcEP', endpoint, urb, 0);
|
||
|
||
#if DBG
|
||
|
||
/* We are assuming that the caller of _OpenEndpoint and _Close endpoint
|
||
* make only serial calls. For this reason we will protect ourselves. If
|
||
* this is violated we return STATUS_DEVICE_BUSY. */
|
||
|
||
if (0 < InterlockedIncrement(&DeviceData->OpenCloseSync)) {
|
||
OpenHCI_KdTrap(("'ohciurb.c: _OpenEndpoint: Non serial call! %d",
|
||
DeviceData->OpenCloseSync));
|
||
urb->UrbHeader.Status = USBD_STATUS_INTERNAL_HC_ERROR;
|
||
ntStatus = STATUS_UNSUCCESSFUL;
|
||
goto CloseEndpoint_Done;
|
||
}
|
||
#endif // DBG
|
||
|
||
if (endpoint == ZERO_LOAD_ENDPOINT(DeviceData)) {
|
||
urb->UrbHeader.Status = USBD_STATUS_SUCCESS;
|
||
ntStatus = STATUS_SUCCESS;
|
||
goto CloseEndpoint_Done;
|
||
}
|
||
|
||
ASSERT_ENDPOINT(endpoint);
|
||
|
||
// mark the endpoint as closed -- this prevents any more transers from
|
||
// being queued, if we fail the close we can clear the close flag.
|
||
// NOTE: if someone is sending transfers while we are closing they have a
|
||
// problem anyway this will just let us finish the close.
|
||
|
||
SET_EPFLAG(endpoint, EP_CLOSED);
|
||
|
||
OpenHCI_LockAndCheckEndpoint(endpoint,
|
||
&outstandingTransfers,
|
||
&activeTransfers,
|
||
&irql);
|
||
|
||
OpenHCI_UnlockEndpoint(endpoint, irql);
|
||
|
||
if (outstandingTransfers ||
|
||
activeTransfers) {
|
||
|
||
//
|
||
// fail if we have pending transfers,
|
||
// note: USBD should prevent this
|
||
//
|
||
|
||
// if we get here we probably have a problem somewhere
|
||
TRAP();
|
||
|
||
urb->UrbHeader.Status = USBD_STATUS_INTERNAL_HC_ERROR;
|
||
OpenHCI_KdPrintDD(DeviceData,
|
||
OHCI_DBG_END_ERROR,
|
||
("'exit OutstandingTRANS OpenHCI_CloseEndpoint\n"));
|
||
ntStatus = STATUS_UNSUCCESSFUL;
|
||
goto CloseEndpoint_Done;
|
||
}
|
||
|
||
ed = endpoint->HcdED;
|
||
|
||
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_END_INFO,
|
||
("'Closing Endpoint: %x\n", endpoint));
|
||
|
||
if (ed) {
|
||
KeAcquireSpinLock(&DeviceData->PausedSpin, &irql);
|
||
|
||
if (ed->PauseFlag != HCD_ED_PAUSE_NOT_PAUSED)
|
||
{
|
||
// This endpoint is paused.
|
||
//
|
||
// we are waiting for the CancelTDsForED to complete, we set a
|
||
// flag here that tells CancelTDsForED to call the remove ED
|
||
// function.
|
||
//
|
||
LOGENTRY(G, 'cle1', endpoint, ed, 0);
|
||
SET_EPFLAG(endpoint, EP_FREE);
|
||
|
||
KeReleaseSpinLock(&DeviceData->PausedSpin, irql);
|
||
|
||
OpenHCI_KdPrintDD(DeviceData,
|
||
OHCI_DBG_END_ERROR, ("'closing paused \n"));
|
||
|
||
freeEP = FALSE;
|
||
|
||
} else {
|
||
|
||
LOGENTRY(G, 'cle2', endpoint, ed, 0);
|
||
KeReleaseSpinLock(&DeviceData->PausedSpin, irql);
|
||
if (ED_EOF != endpoint->ListIndex) {
|
||
RemoveEDForEndpoint(endpoint);
|
||
freeEP = FALSE;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// we return our BW in RemoveEDForEndpoint
|
||
//
|
||
|
||
if (freeEP) {
|
||
ASSERT_ENDPOINT(endpoint);
|
||
// free our descriptors and endpoint
|
||
OpenHCI_UnReserveDescriptors(DeviceData, endpoint->DescriptorsReserved);
|
||
|
||
if (DeviceData->RootHubInterrupt == endpoint) {
|
||
DeviceData->RootHubInterrupt = NULL;
|
||
OpenHCI_KdPrintDD(DeviceData,
|
||
OHCI_DBG_END_TRACE, ("'closed RH interrupt\n"));
|
||
}
|
||
|
||
if (DeviceData->RootHubControl == endpoint) {
|
||
DeviceData->RootHubControl = NULL;
|
||
// root hub is no longer configured
|
||
DeviceData->RootHubConfig = 0;
|
||
OpenHCI_KdPrintDD(DeviceData,
|
||
OHCI_DBG_END_TRACE, ("'closed RH control\n"));
|
||
|
||
if (endpoint->FunctionAddress != 0) {
|
||
DeviceData->RootHubAddress = 0;
|
||
}
|
||
}
|
||
|
||
endpoint->Sig = 0;
|
||
OHCI_ASSERT(!(endpoint->EpFlags & EP_IN_ACTIVE_LIST));
|
||
ExFreePool(endpoint);
|
||
}
|
||
|
||
urb->UrbHeader.Status = USBD_STATUS_SUCCESS;
|
||
|
||
CloseEndpoint_Done:
|
||
|
||
#if DBG
|
||
InterlockedDecrement(&DeviceData->OpenCloseSync);
|
||
|
||
if (!NT_SUCCESS(ntStatus)) {
|
||
// check why we are failing the close
|
||
TEST_TRAP();
|
||
}
|
||
#endif
|
||
|
||
LOGENTRY(G, 'clEP', 0, urb, ntStatus);
|
||
return ntStatus;
|
||
}
|
||
|
||
|
||
ULONG
|
||
Get32BitFrameNumber(
|
||
PHCD_DEVICE_DATA DeviceData
|
||
)
|
||
{
|
||
ULONG hp, fn, n;
|
||
/* This code accounts for the fact that HccaFrameNumber is updated by the
|
||
* HC before the HCD gets an interrupt that will adjust FrameHighPart. No
|
||
* synchronization is nescisary due to great cleaverness. */
|
||
hp = DeviceData->FrameHighPart;
|
||
fn = DeviceData->HCCA->HccaFrameNumber;
|
||
n = ((fn & 0x7FFF) | hp) + ((fn ^ hp) & 0x8000);
|
||
|
||
// Note: we can't log here because this function is called from the ISR
|
||
|
||
return n;
|
||
}
|
||
|
||
|
||
VOID
|
||
OpenHCI_PauseED(
|
||
IN PHCD_ENDPOINT Endpoint
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
Processing of an endpoint by the HC must be paused before any
|
||
maintenance is performend on the TD list. This function first
|
||
sets the skip bit and then places the ED on a separate paused
|
||
list. At the next Start of Frame this ED can then be worked on.
|
||
Presumably the irq function will call CancelTDsForED on this
|
||
endpoint at that time.
|
||
|
||
Arguments:
|
||
Endpoint - then endpoint that need pausing.
|
||
|
||
--*/
|
||
{
|
||
PHCD_DEVICE_DATA DeviceData;
|
||
PHCD_ENDPOINT_DESCRIPTOR ed;
|
||
KIRQL oldirq;
|
||
PHC_OPERATIONAL_REGISTER HC;
|
||
|
||
ASSERT_ENDPOINT(Endpoint);
|
||
DeviceData = Endpoint->DeviceData;
|
||
HC = DeviceData->HC;
|
||
ed = Endpoint->HcdED;
|
||
|
||
OpenHCI_KdPrintDD(DeviceData,
|
||
OHCI_DBG_TD_NOISE, ("'Pausing Endpoint\n"));
|
||
LOGENTRY(G, 'Rpau', DeviceData, Endpoint, ed);
|
||
|
||
KeAcquireSpinLock(&DeviceData->PausedSpin, &oldirq);
|
||
|
||
// Are we already pasued?
|
||
//
|
||
if (ed->PauseFlag == HCD_ED_PAUSE_NOT_PAUSED)
|
||
{
|
||
// No, pause this endpoint
|
||
|
||
ed->HcED.sKip = TRUE;
|
||
|
||
// Clear any currently pending SOF interrupt
|
||
//
|
||
WRITE_REGISTER_ULONG(&HC->HcInterruptStatus, HcInt_StartOfFrame);
|
||
|
||
// It will be safe to operate on the endpoint in the next frame
|
||
//
|
||
ed->ReclamationFrame = Get32BitFrameNumber(DeviceData) + 1;
|
||
|
||
// Put this endpoint on the list of endpoints that OpenHCI_IsrDPC()
|
||
// should pass to OpenHCI_CancelTDsForED()
|
||
//
|
||
InsertTailList(&DeviceData->PausedEDRestart, &ed->PausedLink);
|
||
|
||
// Make sure SOF interrupts are enabled
|
||
//
|
||
WRITE_REGISTER_ULONG(&HC->HcInterruptEnable, HcInt_StartOfFrame);
|
||
|
||
LOGENTRY(G, 'paus', DeviceData, Endpoint, ed);
|
||
}
|
||
#if DBG
|
||
else {
|
||
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_TD_ERROR,
|
||
("'Warning Endpoint already paused!\n"));
|
||
}
|
||
#endif
|
||
|
||
// Indicate that OpenHCI_CancelTDsForED() needs to make a pass through
|
||
// the requests queued on this endpoint.
|
||
//
|
||
ed->PauseFlag = HCD_ED_PAUSE_NEEDED;
|
||
|
||
KeReleaseSpinLock(&DeviceData->PausedSpin, oldirq);
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
OpenHCI_HcControl_OR(
|
||
IN OUT PVOID Context
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
This function is used in connection with KeSynchronizeExecution
|
||
to set the bits of the HcControl register which is accessed by
|
||
all function including the interrupt routine (not just the IsrDPC).
|
||
This function reads the CurrentHcControl field of the device
|
||
extension and ORs in the bits given by NewHcControl. It then
|
||
writes the results to the register.
|
||
Reads of the register take a long time so the value is cached.
|
||
|
||
Results:
|
||
This function modifies the input pointer to HC_CONTROL field
|
||
(PNewHcControl) to the result of the OR operation.
|
||
|
||
--*/
|
||
{
|
||
PHC_CONTROL newControl = &((PKeSynch_HcControl) Context)->NewHcControl;
|
||
PHCD_DEVICE_DATA DeviceData = ((PKeSynch_HcControl) Context)->DeviceData;
|
||
|
||
DeviceData->CurrentHcControl.ul
|
||
= READ_REGISTER_ULONG(&DeviceData->HC->HcControl.ul);
|
||
newControl->ul = (DeviceData->CurrentHcControl.ul |= newControl->ul);
|
||
WRITE_REGISTER_ULONG(&DeviceData->HC->HcControl.ul, newControl->ul);
|
||
return TRUE;
|
||
}
|
||
|
||
BOOLEAN
|
||
OpenHCI_HcControl_AND(
|
||
PVOID Context
|
||
)
|
||
/**
|
||
Routine Description:
|
||
OpenHCI_HcControl_OR with a twist.
|
||
--*/
|
||
{
|
||
PHC_CONTROL newControl = &((PKeSynch_HcControl) Context)->NewHcControl;
|
||
PHCD_DEVICE_DATA DeviceData = ((PKeSynch_HcControl) Context)->DeviceData;
|
||
|
||
DeviceData->CurrentHcControl.ul
|
||
= READ_REGISTER_ULONG(&DeviceData->HC->HcControl.ul);
|
||
newControl->ul = (DeviceData->CurrentHcControl.ul &= newControl->ul);
|
||
WRITE_REGISTER_ULONG(&DeviceData->HC->HcControl.ul, newControl->ul);
|
||
return TRUE;
|
||
}
|
||
|
||
BOOLEAN
|
||
OpenHCI_HcControl_SetHCFS(
|
||
PVOID Context
|
||
)
|
||
/**
|
||
Routine Description:
|
||
Mask out the Functional State in the HcControl register
|
||
and in its place put the ulong.
|
||
--*/
|
||
{
|
||
PHC_CONTROL newControl = &((PKeSynch_HcControl) Context)->NewHcControl;
|
||
PHCD_DEVICE_DATA DeviceData = ((PKeSynch_HcControl) Context)->DeviceData;
|
||
|
||
DeviceData->CurrentHcControl.ul
|
||
= READ_REGISTER_ULONG(&DeviceData->HC->HcControl.ul);
|
||
|
||
newControl->ul &= HcCtrl_HCFS_MASK;
|
||
DeviceData->CurrentHcControl.ul &= ~HcCtrl_HCFS_MASK;
|
||
newControl->ul = (DeviceData->CurrentHcControl.ul |= newControl->ul);
|
||
|
||
WRITE_REGISTER_ULONG(&DeviceData->HC->HcControl.ul, newControl->ul);
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
OpenHCI_ListEnablesAtNextSOF(
|
||
PVOID Context
|
||
)
|
||
/**
|
||
Routine Description:
|
||
Set the ListEnablesAtNextSOF value in DeviceData so that when the
|
||
interrupt comes those lists will be enabled.
|
||
--*/
|
||
{
|
||
((PKeSynch_HcControl) Context)->DeviceData->ListEnablesAtNextSOF.ul
|
||
= HcCtrl_ListEnableMask & ((PKeSynch_HcControl) Context)->
|
||
NewHcControl.ul;
|
||
|
||
return TRUE;
|
||
}
|
||
|