You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2547 lines
71 KiB
2547 lines
71 KiB
/*++
|
|
|
|
Copyright (c) 1999, 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
async.c
|
|
|
|
Abstract:
|
|
|
|
miniport transfer code for control and bulk
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Notes:
|
|
|
|
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
|
|
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
|
|
PURPOSE.
|
|
|
|
Copyright (c) 1999, 2000 Microsoft Corporation. All Rights Reserved.
|
|
|
|
|
|
Revision History:
|
|
|
|
1-1-00 : created, jdunn
|
|
|
|
--*/
|
|
|
|
#include "common.h"
|
|
|
|
|
|
//implements the following miniport functions:
|
|
|
|
//non paged
|
|
//EHCI_OpenControlEndpoint
|
|
//EHCI_InterruptTransfer
|
|
//EHCI_OpenControlEndpoint
|
|
//EHCI_InitializeTD
|
|
//EHCI_InitializeQH
|
|
|
|
|
|
VOID
|
|
EHCI_EnableAsyncList(
|
|
PDEVICE_DATA DeviceData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PHC_OPERATIONAL_REGISTER hcOp;
|
|
USBCMD cmd;
|
|
|
|
hcOp = DeviceData->OperationalRegisters;
|
|
|
|
cmd.ul = READ_REGISTER_ULONG(&hcOp->UsbCommand.ul);
|
|
|
|
cmd.AsyncScheduleEnable = 1;
|
|
|
|
WRITE_REGISTER_ULONG(&hcOp->UsbCommand.ul,
|
|
cmd.ul);
|
|
|
|
LOGENTRY(DeviceData, G, '_enA', cmd.ul, 0, 0);
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
EHCI_DisableAsyncList(
|
|
PDEVICE_DATA DeviceData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PHC_OPERATIONAL_REGISTER hcOp;
|
|
USBCMD cmd;
|
|
USBSTS sts;
|
|
|
|
hcOp = DeviceData->OperationalRegisters;
|
|
|
|
cmd.ul = READ_REGISTER_ULONG(&hcOp->UsbCommand.ul);
|
|
|
|
cmd.AsyncScheduleEnable = 0;
|
|
|
|
WRITE_REGISTER_ULONG(&hcOp->UsbCommand.ul,
|
|
cmd.ul);
|
|
|
|
// Note this just requests the async schedule to disable
|
|
// it is not a synchronous function, the list may be running
|
|
// on return from this function. We synchronize with the real
|
|
// status in the flush function.
|
|
//
|
|
// The reason is that this function is used to optimize perf
|
|
// by turning off the async list when no xfers are availble.
|
|
// We don't want to block the diver waititing for the list to
|
|
// disable.
|
|
|
|
LOGENTRY(DeviceData, G, '_dsL', cmd.ul, 0, 0);
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
EHCI_LinkTransferToQueue(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData,
|
|
PHCD_TRANSFER_DESCRIPTOR FirstTd
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PHCD_QUEUEHEAD_DESCRIPTOR qh;
|
|
BOOLEAN syncWithHw;
|
|
|
|
qh = EndpointData->QueueHead;
|
|
|
|
// now link the transfers to the queue.
|
|
// Two cases to handle:
|
|
//
|
|
// case 1: HeadP is pointing to dummy, no transfer
|
|
// case 2: HeadP points to possibly active transfer
|
|
|
|
LOGENTRY(DeviceData, G, '_L2Q', qh, EndpointData, EndpointData->HcdHeadP);
|
|
|
|
syncWithHw = EHCI_HardwarePresent(DeviceData, FALSE);
|
|
|
|
EHCI_ASSERT(DeviceData, EndpointData->HcdHeadP != NULL);
|
|
if (EndpointData->HcdHeadP == EndpointData->DummyTd) {
|
|
// The hardware will be accessing the dummy QH
|
|
// link it in
|
|
|
|
if (syncWithHw) {
|
|
EHCI_LockQueueHead(DeviceData,
|
|
qh,
|
|
EndpointData->Parameters.TransferType);
|
|
}
|
|
|
|
qh->HwQH.CurrentTD.HwAddress = EndpointData->QhChkPhys;
|
|
|
|
LOGENTRY(DeviceData, G, '_L21', qh, EndpointData, 0);
|
|
|
|
qh->HwQH.Overlay.qTD.AltNext_qTD.HwAddress =
|
|
FirstTd->HwTD.AltNext_qTD.HwAddress;
|
|
qh->HwQH.Overlay.qTD.Next_qTD.HwAddress =
|
|
FirstTd->PhysicalAddress;
|
|
qh->HwQH.Overlay.qTD.Token.BytesToTransfer = 0;
|
|
qh->HwQH.Overlay.qTD.Token.Active = 0;
|
|
qh->HwQH.Overlay.qTD.Token.Halted = 0;
|
|
|
|
if (syncWithHw) {
|
|
EHCI_UnlockQueueHead(DeviceData,
|
|
qh);
|
|
}
|
|
|
|
EndpointData->HcdHeadP = FirstTd;
|
|
} else {
|
|
|
|
PHCD_TRANSFER_DESCRIPTOR td, lastTd;
|
|
PTRANSFER_CONTEXT transfer, tmp;
|
|
ULONG i, active;
|
|
|
|
// new transfer already points to
|
|
// dummyTd
|
|
|
|
// walk the transfer list to the last td
|
|
lastTd = td = EndpointData->HcdHeadP;
|
|
ASSERT_TD(DeviceData, td);
|
|
while (td != EndpointData->DummyTd) {
|
|
lastTd = td;
|
|
td = TRANSFER_DESCRIPTOR_PTR(td->NextHcdTD);
|
|
LOGENTRY(DeviceData, G, '_nx2', qh, td, 0);
|
|
ASSERT_TD(DeviceData, td);
|
|
}
|
|
// note last td should not be dummy, if dummy td were
|
|
// head we would not be in this case
|
|
EHCI_ASSERT(DeviceData, lastTd != EndpointData->DummyTd);
|
|
ASSERT_TD(DeviceData, lastTd);
|
|
LOGENTRY(DeviceData, G, '_lst', qh, lastTd, 0);
|
|
|
|
transfer = TRANSFER_CONTEXT_PTR(lastTd->TransferContext);
|
|
|
|
// note that we cannot lock the queue head here since
|
|
// that might screw up any split transactions we have
|
|
|
|
// instead we use a write loop to take care of any
|
|
// race conditions caused by us accessing the overlay
|
|
// concurrently with the controller
|
|
|
|
|
|
// fixup the alt_next pointers in the TDs of the
|
|
// last transfer
|
|
for (i=0; i<EndpointData->TdCount; i++) {
|
|
td = &EndpointData->TdList->Td[i];
|
|
tmp = TRANSFER_CONTEXT_PTR(td->TransferContext);
|
|
if (tmp == transfer) {
|
|
SET_ALTNEXT_TD(DeviceData, td, FirstTd);
|
|
}
|
|
}
|
|
|
|
// point the last TD at the first TD
|
|
SET_NEXT_TD(DeviceData, lastTd, FirstTd);
|
|
|
|
// now check the overlay, if the last TD is current
|
|
// then we need to update the overlay too
|
|
LOGENTRY(DeviceData, G, '_ckk', qh->HwQH.CurrentTD.HwAddress,
|
|
lastTd, lastTd->PhysicalAddress);
|
|
|
|
if (qh->HwQH.CurrentTD.HwAddress == lastTd->PhysicalAddress) {
|
|
LOGENTRY(DeviceData, G, '_upo', qh->HwQH.CurrentTD.HwAddress,
|
|
qh->HwQH.Overlay.qTD.Next_qTD.HwAddress,
|
|
qh->HwQH.Overlay.qTD.AltNext_qTD.HwAddress);
|
|
|
|
qh->HwQH.Overlay.qTD.Next_qTD.HwAddress =
|
|
FirstTd->PhysicalAddress;
|
|
qh->HwQH.Overlay.qTD.AltNext_qTD.HwAddress =
|
|
EHCI_TERMINATE_BIT;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
PHCD_QUEUEHEAD_DESCRIPTOR
|
|
EHCI_InitializeQH(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData,
|
|
PHCD_QUEUEHEAD_DESCRIPTOR Qh,
|
|
HW_32BIT_PHYSICAL_ADDRESS HwPhysAddress
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize an QH for inserting in to the
|
|
schedule
|
|
|
|
returns a ptr to the QH passed in
|
|
|
|
Arguments:
|
|
|
|
|
|
--*/
|
|
{
|
|
|
|
RtlZeroMemory(Qh, sizeof(*Qh));
|
|
|
|
// make double sure we have the prober alignment
|
|
// on the TD structures
|
|
EHCI_ASSERT(DeviceData, (HwPhysAddress & HW_LINK_FLAGS_MASK) == 0);
|
|
Qh->PhysicalAddress = HwPhysAddress;
|
|
ENDPOINT_DATA_PTR(Qh->EndpointData) = EndpointData;
|
|
Qh->Sig = SIG_HCD_QH;
|
|
|
|
// init the hw descriptor
|
|
Qh->HwQH.EpChars.DeviceAddress = EndpointData->Parameters.DeviceAddress;
|
|
Qh->HwQH.EpChars.EndpointNumber = EndpointData->Parameters.EndpointAddress;
|
|
|
|
switch (EndpointData->Parameters.DeviceSpeed) {
|
|
case LowSpeed:
|
|
Qh->HwQH.EpChars.EndpointSpeed = HcEPCHAR_LowSpeed;
|
|
LOGENTRY(DeviceData, G, '_iLS', EndpointData, 0, 0);
|
|
break;
|
|
case FullSpeed:
|
|
Qh->HwQH.EpChars.EndpointSpeed = HcEPCHAR_FullSpeed;
|
|
LOGENTRY(DeviceData, G, '_iFS', EndpointData, 0, 0);
|
|
break;
|
|
case HighSpeed:
|
|
Qh->HwQH.EpChars.EndpointSpeed = HcEPCHAR_HighSpeed;
|
|
LOGENTRY(DeviceData, G, '_iHS', EndpointData, 0, 0);
|
|
break;
|
|
default:
|
|
USBPORT_BUGCHECK(DeviceData);
|
|
}
|
|
|
|
Qh->HwQH.EpChars.MaximumPacketLength =
|
|
EndpointData->Parameters.MaxPacketSize;
|
|
|
|
Qh->HwQH.EpCaps.HighBWPipeMultiplier = 1;
|
|
if (EndpointData->Parameters.DeviceSpeed == HcEPCHAR_HighSpeed) {
|
|
Qh->HwQH.EpCaps.HubAddress = 0;
|
|
Qh->HwQH.EpCaps.PortNumber = 0;
|
|
} else {
|
|
Qh->HwQH.EpCaps.HubAddress =
|
|
EndpointData->Parameters.TtDeviceAddress;
|
|
Qh->HwQH.EpCaps.PortNumber =
|
|
EndpointData->Parameters.TtPortNumber;
|
|
if (EndpointData->Parameters.TransferType == Control) {
|
|
Qh->HwQH.EpChars.ControlEndpointFlag = 1;
|
|
}
|
|
LOGENTRY(DeviceData, G, '_iTT',
|
|
EndpointData->Parameters.TtPortNumber,
|
|
EndpointData->Parameters.TtDeviceAddress,
|
|
Qh->HwQH.EpChars.ControlEndpointFlag);
|
|
}
|
|
|
|
// init the overlay are such that we are in the 'advance queue'
|
|
// state with the next queue Tds pointing to terminate links
|
|
Qh->HwQH.Overlay.qTD.Next_qTD.HwAddress = EHCI_TERMINATE_BIT;
|
|
Qh->HwQH.Overlay.qTD.AltNext_qTD.HwAddress = EHCI_TERMINATE_BIT;
|
|
Qh->HwQH.Overlay.qTD.Token.Active = 0;
|
|
Qh->HwQH.Overlay.qTD.Token.Halted = 0;
|
|
|
|
return Qh;
|
|
}
|
|
|
|
|
|
PHCD_TRANSFER_DESCRIPTOR
|
|
EHCI_InitializeTD(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData,
|
|
PHCD_TRANSFER_DESCRIPTOR Td,
|
|
HW_32BIT_PHYSICAL_ADDRESS HwPhysAddress
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize an ED for insertin in to the
|
|
schedule
|
|
|
|
returns a ptr to the ED passed in
|
|
|
|
Arguments:
|
|
|
|
|
|
--*/
|
|
{
|
|
RtlZeroMemory(Td, sizeof(*Td));
|
|
|
|
// make double sure we have the prober alignment
|
|
// on the TD structures
|
|
EHCI_ASSERT(DeviceData, (HwPhysAddress & HW_LINK_FLAGS_MASK) == 0);
|
|
Td->PhysicalAddress = HwPhysAddress;
|
|
ENDPOINT_DATA_PTR(Td->EndpointData) = EndpointData;
|
|
Td->Sig = SIG_HCD_TD;
|
|
TRANSFER_CONTEXT_PTR(Td->TransferContext) = FREE_TD_CONTEXT;
|
|
|
|
return Td;
|
|
}
|
|
|
|
|
|
USB_MINIPORT_STATUS
|
|
EHCI_ControlTransfer(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData,
|
|
PTRANSFER_PARAMETERS TransferParameters,
|
|
PTRANSFER_CONTEXT TransferContext,
|
|
PTRANSFER_SG_LIST TransferSGList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize a control transfer
|
|
|
|
|
|
NOTES:
|
|
|
|
HW pointers nextTD and AltNextTD are shadowed in
|
|
NextHcdTD and AltNextHcdTD.
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
--*/
|
|
{
|
|
PHCD_TRANSFER_DESCRIPTOR prevTd, td, setupTd, statusTd;
|
|
ULONG lengthMapped, dataTDCount = 0;
|
|
ULONG nextToggle;
|
|
|
|
|
|
// we can do any control transfer with six TDs
|
|
if (EndpointData->FreeTds < 6) {
|
|
return USBMP_STATUS_BUSY;
|
|
}
|
|
|
|
EndpointData->PendingTransfers++;
|
|
DeviceData->PendingControlAndBulk++;
|
|
|
|
nextToggle = HcTOK_Toggle1;
|
|
|
|
// we have enough tds, program the transfer
|
|
|
|
//
|
|
// first prepare a TD for the setup packet
|
|
//
|
|
|
|
LOGENTRY(DeviceData, G, '_CTR', EndpointData, TransferParameters, 0);
|
|
|
|
//
|
|
// allocate setup stage
|
|
//
|
|
TransferContext->PendingTds++;
|
|
setupTd = EHCI_ALLOC_TD(DeviceData, EndpointData);
|
|
if (!setupTd) {
|
|
goto EHCI_ControlTransferNoTds;
|
|
}
|
|
INITIALIZE_TD_FOR_TRANSFER(setupTd, TransferContext);
|
|
|
|
//
|
|
// Move setup data into TD (8 chars long)
|
|
//
|
|
RtlCopyMemory(&setupTd->Packet[0],
|
|
&TransferParameters->SetupPacket[0],
|
|
8);
|
|
|
|
// this will set the offset and phys address bits at
|
|
// the same time
|
|
setupTd->HwTD.BufferPage[0].ul = (ULONG)(((PCHAR) &setupTd->Packet[0])
|
|
- ((PCHAR) &setupTd->HwTD)) + setupTd->PhysicalAddress;
|
|
|
|
setupTd->HwTD.Token.BytesToTransfer = 8;
|
|
setupTd->HwTD.Token.Pid = HcTOK_Setup;
|
|
setupTd->HwTD.Token.DataToggle = HcTOK_Toggle0;
|
|
setupTd->HwTD.Token.Active = 1;
|
|
|
|
|
|
LOGENTRY(DeviceData,
|
|
G, '_set',
|
|
setupTd,
|
|
*((PLONG) &TransferParameters->SetupPacket[0]),
|
|
*((PLONG) &TransferParameters->SetupPacket[4]));
|
|
|
|
// allocate the status phase TD now so we can
|
|
// point the data TDs to it
|
|
TransferContext->PendingTds++;
|
|
statusTd = EHCI_ALLOC_TD(DeviceData, EndpointData);
|
|
if (!statusTd) {
|
|
goto EHCI_ControlTransferNoTds;
|
|
}
|
|
INITIALIZE_TD_FOR_TRANSFER(statusTd, TransferContext);
|
|
|
|
// point setup to status
|
|
SET_ALTNEXT_TD(DeviceData, setupTd, statusTd);
|
|
|
|
//
|
|
// now setup the data phase
|
|
//
|
|
|
|
td = prevTd = setupTd;
|
|
lengthMapped = 0;
|
|
while (lengthMapped < TransferParameters->TransferBufferLength) {
|
|
|
|
//
|
|
// fields for data TD
|
|
//
|
|
|
|
td = EHCI_ALLOC_TD(DeviceData, EndpointData);
|
|
if (!td) {
|
|
goto EHCI_ControlTransferNoTds;
|
|
}
|
|
INITIALIZE_TD_FOR_TRANSFER(td, TransferContext);
|
|
dataTDCount++;
|
|
TransferContext->PendingTds++;
|
|
SET_NEXT_TD(DeviceData, prevTd, td);
|
|
|
|
// use direction specified in transfer
|
|
if (TEST_FLAG(TransferParameters->TransferFlags,
|
|
USBD_TRANSFER_DIRECTION_IN)) {
|
|
td->HwTD.Token.Pid = HcTOK_In;
|
|
} else {
|
|
td->HwTD.Token.Pid = HcTOK_Out;
|
|
}
|
|
|
|
td->HwTD.Token.DataToggle = nextToggle;
|
|
td->HwTD.Token.Active = 1;
|
|
|
|
SET_ALTNEXT_TD(DeviceData, td, statusTd);
|
|
|
|
LOGENTRY(DeviceData,
|
|
G, '_dta', td, lengthMapped,
|
|
TransferParameters->TransferBufferLength);
|
|
|
|
lengthMapped =
|
|
EHCI_MapAsyncTransferToTd(DeviceData,
|
|
EndpointData->Parameters.MaxPacketSize,
|
|
lengthMapped,
|
|
&nextToggle,
|
|
TransferContext,
|
|
td,
|
|
TransferSGList);
|
|
|
|
// calculate next data toggle
|
|
// if number of packets is odd the nextToggle is 0
|
|
// otherwise it is 1
|
|
|
|
|
|
|
|
|
|
prevTd = td;
|
|
}
|
|
|
|
// last td prepared points to status
|
|
SET_NEXT_TD(DeviceData, td, statusTd);
|
|
|
|
//
|
|
// now do the status phase
|
|
//
|
|
|
|
LOGENTRY(DeviceData, G, '_sta', statusTd, 0, dataTDCount);
|
|
|
|
// do the status phase
|
|
|
|
// no buffer
|
|
statusTd->HwTD.BufferPage[0].ul = 0;
|
|
|
|
statusTd->HwTD.Token.BytesToTransfer = 0;
|
|
statusTd->TransferLength = 0;
|
|
// status stage is always toggle 1
|
|
statusTd->HwTD.Token.DataToggle = HcTOK_Toggle1;
|
|
statusTd->HwTD.Token.Active = 1;
|
|
statusTd->HwTD.Token.InterruptOnComplete = 1;
|
|
|
|
// status phase is opposite data dirrection
|
|
if (TEST_FLAG(TransferParameters->TransferFlags, USBD_TRANSFER_DIRECTION_IN)) {
|
|
statusTd->HwTD.Token.Pid = HcTOK_Out;
|
|
} else {
|
|
statusTd->HwTD.Token.Pid = HcTOK_In;
|
|
}
|
|
|
|
// put the request on the hardware queue
|
|
LOGENTRY(DeviceData, G,
|
|
'_Tal', TransferContext->PendingTds, td->PhysicalAddress, td);
|
|
|
|
// td points to last TD in this transfer, point it at the dummy
|
|
SET_NEXT_TD(DeviceData, statusTd, EndpointData->DummyTd);
|
|
|
|
// set the active bit in the setup Phase TD, this will
|
|
// activate the transfer
|
|
|
|
PCI_TRIGGER(DeviceData->OperationalRegisters);
|
|
|
|
// tell the hc we have control transfers available
|
|
// do this vefore we link in because we will try
|
|
// to sync with the hardware
|
|
EHCI_EnableAsyncList(DeviceData);
|
|
|
|
|
|
EHCI_LinkTransferToQueue(DeviceData,
|
|
EndpointData,
|
|
setupTd);
|
|
|
|
ASSERT_DUMMY_TD(DeviceData, EndpointData->DummyTd);
|
|
|
|
|
|
return USBMP_STATUS_SUCCESS;
|
|
|
|
EHCI_ControlTransferNoTds:
|
|
|
|
// Should never get here!
|
|
USBPORT_BUGCHECK(DeviceData);
|
|
return USBMP_STATUS_BUSY;
|
|
}
|
|
|
|
|
|
USB_MINIPORT_STATUS
|
|
EHCI_BulkTransfer(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData,
|
|
PTRANSFER_PARAMETERS TransferUrb,
|
|
PTRANSFER_CONTEXT TransferContext,
|
|
PTRANSFER_SG_LIST TransferSGList
|
|
)
|
|
{
|
|
PHCD_TRANSFER_DESCRIPTOR firstTd, prevTd, td, tailTd;
|
|
ULONG lengthMapped;
|
|
ULONG need;
|
|
|
|
// figure out how many TDs we will need
|
|
need = TransferUrb->TransferBufferLength/(16*1024)+1;
|
|
|
|
if (need > EndpointData->FreeTds) {
|
|
LOGENTRY(DeviceData, G, '_BBS', EndpointData, TransferUrb, 0);
|
|
return USBMP_STATUS_BUSY;
|
|
}
|
|
|
|
EndpointData->PendingTransfers++;
|
|
DeviceData->PendingControlAndBulk++;
|
|
|
|
// we have enough tds, program the transfer
|
|
|
|
LOGENTRY(DeviceData, G, '_BIT', EndpointData, TransferUrb, 0);
|
|
|
|
lengthMapped = 0;
|
|
prevTd = NULL;
|
|
while (lengthMapped < TransferUrb->TransferBufferLength) {
|
|
|
|
TransferContext->PendingTds++;
|
|
td = EHCI_ALLOC_TD(DeviceData, EndpointData);
|
|
INITIALIZE_TD_FOR_TRANSFER(td, TransferContext);
|
|
|
|
if (TransferContext->PendingTds == 1) {
|
|
firstTd = td;
|
|
} else {
|
|
SET_NEXT_TD(DeviceData, prevTd, td);
|
|
}
|
|
SET_ALTNEXT_TD(DeviceData, td, EndpointData->DummyTd);
|
|
|
|
//
|
|
// fields for data TD
|
|
//
|
|
td->HwTD.Token.InterruptOnComplete = 1;
|
|
|
|
// use direction specified in transfer
|
|
if (TEST_FLAG(TransferUrb->TransferFlags, USBD_TRANSFER_DIRECTION_IN)) {
|
|
td->HwTD.Token.Pid = HcTOK_In;
|
|
} else {
|
|
td->HwTD.Token.Pid = HcTOK_Out;
|
|
}
|
|
|
|
td->HwTD.Token.DataToggle = HcTOK_Toggle1;
|
|
td->HwTD.Token.Active = 1;
|
|
|
|
LOGENTRY(DeviceData,
|
|
G, '_dta', td, lengthMapped, TransferUrb->TransferBufferLength);
|
|
|
|
lengthMapped =
|
|
EHCI_MapAsyncTransferToTd(DeviceData,
|
|
EndpointData->Parameters.MaxPacketSize,
|
|
lengthMapped,
|
|
NULL,
|
|
TransferContext,
|
|
td,
|
|
TransferSGList);
|
|
|
|
prevTd = td;
|
|
}
|
|
|
|
// special case the zero length transfer
|
|
if (TransferUrb->TransferBufferLength == 0) {
|
|
|
|
TEST_TRAP();
|
|
|
|
TransferContext->PendingTds++;
|
|
td = EHCI_ALLOC_TD(DeviceData, EndpointData);
|
|
INITIALIZE_TD_FOR_TRANSFER(td, TransferContext);
|
|
|
|
EHCI_ASSERT(DeviceData, TransferContext->PendingTds == 1);
|
|
firstTd = td;
|
|
SET_ALTNEXT_TD(DeviceData, td, EndpointData->DummyTd);
|
|
|
|
td->HwTD.Token.InterruptOnComplete = 1;
|
|
|
|
// use direction specified in transfer
|
|
if (TEST_FLAG(TransferUrb->TransferFlags, USBD_TRANSFER_DIRECTION_IN)) {
|
|
td->HwTD.Token.Pid = HcTOK_In;
|
|
} else {
|
|
td->HwTD.Token.Pid = HcTOK_Out;
|
|
}
|
|
|
|
td->HwTD.Token.DataToggle = HcTOK_Toggle1;
|
|
td->HwTD.Token.Active = 1;
|
|
|
|
// point to a dummy buffer
|
|
td->HwTD.BufferPage[0].ul =
|
|
td->PhysicalAddress;
|
|
|
|
td->HwTD.Token.BytesToTransfer =
|
|
0;
|
|
td->TransferLength = 0;
|
|
}
|
|
|
|
// td points to last TD in this transfer, point it at the dummy
|
|
SET_NEXT_TD(DeviceData, td, EndpointData->DummyTd);
|
|
|
|
// put the request on the hardware queue
|
|
LOGENTRY(DeviceData, G,
|
|
'_Tal', TransferContext->PendingTds, td->PhysicalAddress, td);
|
|
LOGENTRY(DeviceData, G,
|
|
'_ftd', 0, 0, firstTd);
|
|
|
|
// we now have a complete setup of TDs representing this transfer
|
|
// (Next) firstTd(1)->{td}(2)->{td}(3)->td(4)->dummyTd(tbit)
|
|
// (AltNext) all point to dummyTd (tbit)
|
|
//TEST_TRAP();
|
|
// tell the hc we have control transfers available
|
|
EHCI_EnableAsyncList(DeviceData);
|
|
|
|
EHCI_LinkTransferToQueue(DeviceData,
|
|
EndpointData,
|
|
firstTd);
|
|
|
|
ASSERT_DUMMY_TD(DeviceData, EndpointData->DummyTd);
|
|
|
|
return USBMP_STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
USB_MINIPORT_STATUS
|
|
EHCI_OpenBulkOrControlEndpoint(
|
|
PDEVICE_DATA DeviceData,
|
|
BOOLEAN Control,
|
|
PENDPOINT_PARAMETERS EndpointParameters,
|
|
PENDPOINT_DATA EndpointData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PUCHAR buffer;
|
|
HW_32BIT_PHYSICAL_ADDRESS phys, qhPhys;
|
|
PHCD_QUEUEHEAD_DESCRIPTOR qh;
|
|
ULONG i;
|
|
ULONG tdCount, bytes;
|
|
PHCD_TRANSFER_DESCRIPTOR dummyTd;
|
|
|
|
LOGENTRY(DeviceData, G, '_opC', 0, 0, EndpointParameters);
|
|
|
|
InitializeListHead(&EndpointData->DoneTdList);
|
|
|
|
buffer = EndpointParameters->CommonBufferVa;
|
|
phys = EndpointParameters->CommonBufferPhys;
|
|
// how much did we get
|
|
bytes = EndpointParameters->CommonBufferBytes;
|
|
|
|
// 256 byte block used to check for overlay sync
|
|
// problems
|
|
EndpointData->QhChkPhys = phys;
|
|
EndpointData->QhChk = buffer;
|
|
RtlZeroMemory(buffer, 256);
|
|
phys += 256;
|
|
buffer += 256;
|
|
bytes -= 256;
|
|
|
|
// make the Ed
|
|
qh = (PHCD_QUEUEHEAD_DESCRIPTOR) buffer;
|
|
qhPhys = phys;
|
|
// how much did we get
|
|
|
|
phys += sizeof(HCD_QUEUEHEAD_DESCRIPTOR);
|
|
buffer += sizeof(HCD_QUEUEHEAD_DESCRIPTOR);
|
|
bytes -= sizeof(HCD_QUEUEHEAD_DESCRIPTOR);
|
|
|
|
tdCount = bytes/sizeof(HCD_TRANSFER_DESCRIPTOR);
|
|
EHCI_ASSERT(DeviceData, tdCount >= TDS_PER_CONTROL_ENDPOINT);
|
|
|
|
if (EndpointParameters->TransferType == Control) {
|
|
SET_FLAG(EndpointData->Flags, EHCI_EDFLAG_NOHALT);
|
|
}
|
|
|
|
EndpointData->TdList = (PHCD_TD_LIST) buffer;
|
|
EndpointData->TdCount = tdCount;
|
|
for (i=0; i<tdCount; i++) {
|
|
EHCI_InitializeTD(DeviceData,
|
|
EndpointData,
|
|
&EndpointData->TdList->Td[i],
|
|
phys);
|
|
|
|
phys += sizeof(HCD_TRANSFER_DESCRIPTOR);
|
|
}
|
|
EndpointData->FreeTds = tdCount;
|
|
|
|
EndpointData->QueueHead =
|
|
EHCI_InitializeQH(DeviceData,
|
|
EndpointData,
|
|
qh,
|
|
qhPhys);
|
|
|
|
if (Control) {
|
|
// use data toggle in the TDs for control
|
|
qh->HwQH.EpChars.DataToggleControl = HcEPCHAR_Toggle_From_qTD;
|
|
EHCI_ASSERT(DeviceData, tdCount >= TDS_PER_CONTROL_ENDPOINT);
|
|
EndpointData->HcdHeadP = NULL;
|
|
|
|
} else {
|
|
PHCD_TRANSFER_DESCRIPTOR dummyTd;
|
|
|
|
qh->HwQH.EpChars.DataToggleControl = HcEPCHAR_Ignore_Toggle;
|
|
EHCI_ASSERT(DeviceData, tdCount >= TDS_PER_BULK_ENDPOINT);
|
|
//qh->HwQH.EpChars.NakReloadCount = 4;
|
|
|
|
}
|
|
|
|
// allocate a dummy TD for short tranfsers
|
|
// the dummy TD is usd to mark the end of the cuurent transfer
|
|
//
|
|
dummyTd = EHCI_ALLOC_TD(DeviceData, EndpointData);
|
|
dummyTd->HwTD.Next_qTD.HwAddress = EHCI_TERMINATE_BIT;
|
|
TRANSFER_DESCRIPTOR_PTR(dummyTd->NextHcdTD) = NULL;
|
|
dummyTd->HwTD.AltNext_qTD.HwAddress = EHCI_TERMINATE_BIT;
|
|
TRANSFER_DESCRIPTOR_PTR(dummyTd->AltNextHcdTD) = NULL;
|
|
dummyTd->HwTD.Token.Active = 0;
|
|
SET_FLAG(dummyTd->Flags, TD_FLAG_DUMMY);
|
|
EndpointData->DummyTd = dummyTd;
|
|
EndpointData->HcdHeadP = dummyTd;
|
|
|
|
// endpoint is not active, set up the overlay
|
|
// so that the currentTD is the Dummy
|
|
|
|
qh->HwQH.CurrentTD.HwAddress = dummyTd->PhysicalAddress;
|
|
qh->HwQH.Overlay.qTD.Next_qTD.HwAddress = EHCI_TERMINATE_BIT;
|
|
qh->HwQH.Overlay.qTD.AltNext_qTD.HwAddress = EHCI_TERMINATE_BIT;
|
|
qh->HwQH.Overlay.qTD.Token.BytesToTransfer = 0;
|
|
qh->HwQH.Overlay.qTD.Token.Active = 0;
|
|
|
|
// we now have an inactive QueueHead and a Dummy
|
|
// tail TD
|
|
|
|
return USBMP_STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
VOID
|
|
EHCI_InsertQueueHeadInAsyncList(
|
|
PDEVICE_DATA DeviceData,
|
|
PHCD_QUEUEHEAD_DESCRIPTOR Qh
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Insert an aync endpoint (queue head)
|
|
into the HW list
|
|
|
|
Arguments:
|
|
|
|
|
|
--*/
|
|
{
|
|
PHCD_QUEUEHEAD_DESCRIPTOR asyncQh, nextQh;
|
|
HW_LINK_POINTER newLink;
|
|
|
|
asyncQh = DeviceData->AsyncQueueHead;
|
|
|
|
LOGENTRY(DeviceData, G, '_Ain', 0, Qh, asyncQh);
|
|
EHCI_ASSERT(DeviceData, !TEST_FLAG(Qh->QhFlags, EHCI_QH_FLAG_IN_SCHEDULE));
|
|
|
|
// ASYNC QUEUE looks like this:
|
|
//
|
|
//
|
|
// |- we insert here
|
|
//|static QH|<->|xfer QH|<->|xfer QH|<->
|
|
// | |
|
|
// ---------------<->--------------
|
|
|
|
// link new qh to the current 'head' ie
|
|
// first transfer QH
|
|
nextQh = QH_DESCRIPTOR_PTR(asyncQh->NextQh);
|
|
|
|
Qh->HwQH.HLink.HwAddress =
|
|
asyncQh->HwQH.HLink.HwAddress;
|
|
QH_DESCRIPTOR_PTR(Qh->NextQh) = nextQh;
|
|
QH_DESCRIPTOR_PTR(Qh->PrevQh) = asyncQh;
|
|
|
|
QH_DESCRIPTOR_PTR(nextQh->PrevQh) = Qh;
|
|
|
|
// put the new qh at the head of the queue
|
|
|
|
newLink.HwAddress = Qh->PhysicalAddress;
|
|
SET_QH(newLink.HwAddress);
|
|
asyncQh->HwQH.HLink.HwAddress = newLink.HwAddress;
|
|
QH_DESCRIPTOR_PTR(asyncQh->NextQh) = Qh;
|
|
|
|
SET_FLAG(Qh->QhFlags, EHCI_QH_FLAG_IN_SCHEDULE);
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
EHCI_RemoveQueueHeadFromAsyncList(
|
|
PDEVICE_DATA DeviceData,
|
|
PHCD_QUEUEHEAD_DESCRIPTOR Qh
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Remove a aync endpoint (queue head)
|
|
into the HW list
|
|
|
|
Arguments:
|
|
|
|
|
|
--*/
|
|
{
|
|
PHCD_QUEUEHEAD_DESCRIPTOR nextQh, prevQh, asyncQh;
|
|
HW_LINK_POINTER newLink;
|
|
HW_LINK_POINTER asyncHwQh;
|
|
HW_32BIT_PHYSICAL_ADDRESS tmp;
|
|
PHC_OPERATIONAL_REGISTER hcOp;
|
|
|
|
hcOp = DeviceData->OperationalRegisters;
|
|
|
|
LOGENTRY(DeviceData, G, '_Arm', Qh, 0, 0);
|
|
// if already removed bail
|
|
if (!TEST_FLAG(Qh->QhFlags, EHCI_QH_FLAG_IN_SCHEDULE)) {
|
|
return;
|
|
}
|
|
|
|
nextQh = QH_DESCRIPTOR_PTR(Qh->NextQh);
|
|
prevQh = QH_DESCRIPTOR_PTR(Qh->PrevQh);;
|
|
|
|
// ASYNC QUEUE looks like this:
|
|
//
|
|
//|static QH|->|xfer QH|->|xfer QH|->
|
|
// | |
|
|
// -------------<----------------
|
|
|
|
asyncQh = DeviceData->AsyncQueueHead;
|
|
asyncHwQh.HwAddress = asyncQh->PhysicalAddress;
|
|
SET_QH(asyncHwQh.HwAddress);
|
|
|
|
// unlink
|
|
LOGENTRY(DeviceData, G, '_ulk', Qh, prevQh, nextQh);
|
|
newLink.HwAddress = nextQh->PhysicalAddress;
|
|
SET_QH(newLink.HwAddress);
|
|
prevQh->HwQH.HLink.HwAddress =
|
|
newLink.HwAddress;
|
|
QH_DESCRIPTOR_PTR(prevQh->NextQh) = nextQh;
|
|
QH_DESCRIPTOR_PTR(nextQh->PrevQh) = prevQh;
|
|
|
|
// flush the HW cache after an unlink, the scheduke
|
|
// should be enabled if we are removeing a QH
|
|
EHCI_AsyncCacheFlush(DeviceData);
|
|
|
|
// we need to update the async list base address reg in case this
|
|
// qh is the current qh, if it is we will just replace it with
|
|
// the static version
|
|
tmp = READ_REGISTER_ULONG(&hcOp->AsyncListAddr);
|
|
|
|
if (tmp == Qh->PhysicalAddress) {
|
|
WRITE_REGISTER_ULONG(&hcOp->AsyncListAddr,
|
|
asyncHwQh.HwAddress);
|
|
}
|
|
|
|
CLEAR_FLAG(Qh->QhFlags, EHCI_QH_FLAG_IN_SCHEDULE);
|
|
}
|
|
|
|
|
|
// figure out which sgentry a particular offset in to
|
|
// a client buffer falls
|
|
#define GET_SG_INDEX(sg, i, offset)\
|
|
for((i)=0; (i) < (sg)->SgCount; (i)++) {\
|
|
if ((offset) >= (sg)->SgEntry[(i)].StartOffset &&\
|
|
(offset) < (sg)->SgEntry[(i)].StartOffset+\
|
|
(sg)->SgEntry[(i)].Length) {\
|
|
break;\
|
|
}\
|
|
}
|
|
|
|
#define GET_SG_OFFSET(sg, i, offset, sgoffset)\
|
|
(sgoffset) = (offset) - (sg)->SgEntry[(i)].StartOffset
|
|
|
|
|
|
ULONG
|
|
EHCI_MapAsyncTransferToTd(
|
|
PDEVICE_DATA DeviceData,
|
|
ULONG MaxPacketSize,
|
|
ULONG LengthMapped,
|
|
PULONG NextToggle,
|
|
PTRANSFER_CONTEXT TransferContext,
|
|
PHCD_TRANSFER_DESCRIPTOR Td,
|
|
PTRANSFER_SG_LIST SgList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Maps a data buffer to TDs according to EHCI rules
|
|
|
|
An EHCI TD can cover up to 20k with 5 page crossing.
|
|
Note that 20k is the most data a single td can describe
|
|
|
|
Each sg entry represents one 4k EHCI 'page'
|
|
|
|
x = pagebreak
|
|
c = current ptr
|
|
b = buffer start
|
|
e = buffer end
|
|
|
|
|
|
{..sg[sgIdx]..}
|
|
b...|---
|
|
x--c----
|
|
[ ]
|
|
\
|
|
sgOffset
|
|
[ ]
|
|
\
|
|
LengthMapped
|
|
|
|
|
|
Worst case for 20k transfer has 5 page breaks and requires
|
|
but 6 bp entries
|
|
|
|
{..sg0..}{..sg1..}{..sg2..}{..sg3..}{..sg4..}{..sg5..}
|
|
| | | | | | |
|
|
x--------x--------x--------x--------x--------x--------x
|
|
b-------------------------------------------->e
|
|
<..bp0..><..bp1..><..bp2..><..bp3..><..bp4..>
|
|
|
|
|
|
|
|
case 1: (<6 sg entries remain)
|
|
(A)- transfer < 16k, page breaks (if c=b sgOffset = 0)
|
|
|
|
{..sg0..}{..sg1..}{..sg2..}{..sg3..}{..sg4..}
|
|
| | | | | |
|
|
x--------x--------x--------x--------x--------x
|
|
b------------------------------------>e
|
|
<..bp0..><..bp1..><..bp2..><..bp3..><..bp4..>
|
|
[.................iTD.................]
|
|
|
|
(B)- last part of a transfer
|
|
|
|
{..sgN..}{.sgN+1.}{.sgN+2.}{.sgN+3.}{.sgN+4.}
|
|
| | | | | |
|
|
x--------x--------x--------x--------x--------x
|
|
b.....|.c------------------------------------->e
|
|
<..bp0..><..bp1..><..bp2..><..bp3..><..bp4..>
|
|
[..................iTD.................]
|
|
|
|
case 2: (>5 sg entries remain)
|
|
(A)- transfer > 20k, first part of a large transfer
|
|
|
|
{..sg0..}{..sg1..}{..sg2..}{..sg3..}{..sg4..}{..sg5..}
|
|
| | | | | | |
|
|
x--------x--------x--------x--------x--------x--------x
|
|
b-------------------------------------------->e
|
|
<..bp0..><..bp1..><..bp2..><..bp3..><..bp4..>
|
|
[....................iTD................]
|
|
|
|
(B)- continuation of a large transfer
|
|
|
|
|
|
Interesting DMA tests (USBTEST):
|
|
|
|
length, offset - cases hit
|
|
|
|
|
|
Arguments:
|
|
|
|
Returns:
|
|
|
|
LengthMapped
|
|
|
|
--*/
|
|
{
|
|
ULONG sgIdx, sgOffset, bp, i;
|
|
ULONG lengthThisTd;
|
|
PTRANSFER_PARAMETERS tp;
|
|
|
|
// A TD can have up to 5 page crossing. This means we
|
|
// can put 5 sg entries in to one TD.
|
|
|
|
// point to first entry
|
|
|
|
LOGENTRY(DeviceData, G, '_Mpr', TransferContext,
|
|
0, LengthMapped);
|
|
|
|
EHCI_ASSERT(DeviceData, SgList->SgCount != 0);
|
|
|
|
tp = TransferContext->TransferParameters;
|
|
|
|
GET_SG_INDEX(SgList, sgIdx, LengthMapped);
|
|
LOGENTRY(DeviceData, G, '_Mpp', SgList, 0, sgIdx);
|
|
EHCI_ASSERT(DeviceData, sgIdx < SgList->SgCount);
|
|
|
|
if ((SgList->SgCount-sgIdx) < 6) {
|
|
// first case, <6 entries left
|
|
// ie <20k, we can fit this in
|
|
// a single TD.
|
|
|
|
#if DBG
|
|
if (sgIdx == 0) {
|
|
// case 1A
|
|
// USBT dma test length 4096, offset 0
|
|
// will hit this case
|
|
// TEST_TRAP();
|
|
LOGENTRY(DeviceData, G, '_c1a', SgList, 0, sgIdx);
|
|
} else {
|
|
// case 1B
|
|
// USBT dma test length 8192 offset 512
|
|
// will hit this case
|
|
LOGENTRY(DeviceData, G, '_c1b', SgList, 0, sgIdx);
|
|
//TEST_TRAP();
|
|
}
|
|
#endif
|
|
lengthThisTd = tp->TransferBufferLength - LengthMapped;
|
|
|
|
// compute offset into this TD
|
|
GET_SG_OFFSET(SgList, sgIdx, LengthMapped, sgOffset);
|
|
LOGENTRY(DeviceData, G, '_sgO', sgOffset, sgIdx, LengthMapped);
|
|
|
|
// adjust for the amount of buffer consumed by the
|
|
// previous TD
|
|
|
|
// sets current offset and address at the same time
|
|
Td->HwTD.BufferPage[0].ul =
|
|
SgList->SgEntry[sgIdx].LogicalAddress.Hw32 + sgOffset;
|
|
|
|
i = sgIdx+1;
|
|
for (bp = 1; bp < 5 && i < SgList->SgCount; bp++,i++) {
|
|
Td->HwTD.BufferPage[bp].ul =
|
|
SgList->SgEntry[i].LogicalAddress.Hw32;
|
|
EHCI_ASSERT(DeviceData, Td->HwTD.BufferPage[bp].CurrentOffset == 0);
|
|
}
|
|
|
|
LOGENTRY(DeviceData, G, '_sg1', Td->HwTD.BufferPage[0].ul, 0,
|
|
0);
|
|
|
|
} else {
|
|
// second case, >=6 entries left
|
|
// we will need more than one TD
|
|
ULONG adjust, packetCount;
|
|
#if DBG
|
|
if (sgIdx == 0) {
|
|
// case 2A
|
|
// USBT dma test length 8192 offset 512
|
|
// will hit this case
|
|
LOGENTRY(DeviceData, G, '_c2a', SgList, 0, sgIdx);
|
|
//TEST_TRAP();
|
|
} else {
|
|
// case 2B
|
|
// USBT dma test length 12288 offset 1
|
|
// will hit this case
|
|
LOGENTRY(DeviceData, G, '_c2b', SgList, 0, sgIdx);
|
|
//TEST_TRAP();
|
|
}
|
|
#endif
|
|
// sg offset is the offset in to the current TD to start
|
|
// using
|
|
// ie it is the number of bytes already consumed by the
|
|
// previous td
|
|
GET_SG_OFFSET(SgList, sgIdx, LengthMapped, sgOffset);
|
|
LOGENTRY(DeviceData, G, '_sgO', sgOffset, sgIdx, LengthMapped);
|
|
#if DBG
|
|
if (sgIdx == 0) {
|
|
EHCI_ASSERT(DeviceData, sgOffset == 0);
|
|
}
|
|
#endif
|
|
//
|
|
// consume the next 4 sgEntries
|
|
//
|
|
|
|
// sets currentOffset at the same time
|
|
Td->HwTD.BufferPage[0].ul =
|
|
SgList->SgEntry[sgIdx].LogicalAddress.Hw32+sgOffset;
|
|
lengthThisTd = EHCI_PAGE_SIZE - Td->HwTD.BufferPage[0].CurrentOffset;
|
|
|
|
i = sgIdx+1;
|
|
for (bp = 1; bp < 5; bp++,i++) {
|
|
Td->HwTD.BufferPage[bp].ul =
|
|
SgList->SgEntry[i].LogicalAddress.Hw32;
|
|
EHCI_ASSERT(DeviceData, Td->HwTD.BufferPage[bp].CurrentOffset == 0);
|
|
EHCI_ASSERT(DeviceData, i < SgList->SgCount);
|
|
lengthThisTd += EHCI_PAGE_SIZE;
|
|
}
|
|
|
|
// round TD length down to the highest multiple
|
|
// of max_packet size
|
|
|
|
packetCount = lengthThisTd/MaxPacketSize;
|
|
LOGENTRY(DeviceData, G, '_sg2', MaxPacketSize, packetCount, lengthThisTd);
|
|
|
|
adjust = lengthThisTd - packetCount*MaxPacketSize;
|
|
|
|
if (adjust) {
|
|
lengthThisTd-=adjust;
|
|
LOGENTRY(DeviceData, G, '_adj', adjust, lengthThisTd, 0);
|
|
}
|
|
|
|
if (NextToggle) {
|
|
// calculate next data toggle if requested
|
|
// two cases
|
|
// case 1: prev NextToggle is 1
|
|
// if number of packets is odd the nextToggle is 0
|
|
// otherwise it is 1
|
|
// case 2: prev NextToggle is 0
|
|
// if number of packets is odd the nextToggle is 1
|
|
// otherwise it is 0
|
|
|
|
// so if packet count is even the value remains unchanged
|
|
// otherwise we have to toggle it.
|
|
if (packetCount % 2) {
|
|
// packet count this TD is odd
|
|
*NextToggle = (*NextToggle) ? 0 : 1;
|
|
}
|
|
}
|
|
|
|
EHCI_ASSERT(DeviceData, lengthThisTd != 0);
|
|
EHCI_ASSERT(DeviceData, lengthThisTd >= SgList->SgEntry[sgIdx].Length);
|
|
|
|
}
|
|
|
|
LengthMapped += lengthThisTd;
|
|
Td->HwTD.Token.BytesToTransfer =
|
|
lengthThisTd;
|
|
Td->TransferLength = lengthThisTd;
|
|
|
|
LOGENTRY(DeviceData, G, '_Mp1', LengthMapped, lengthThisTd, Td);
|
|
|
|
return LengthMapped;
|
|
}
|
|
|
|
|
|
VOID
|
|
EHCI_SetAsyncEndpointState(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData,
|
|
MP_ENDPOINT_STATE State
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PHCD_QUEUEHEAD_DESCRIPTOR qh;
|
|
PHC_OPERATIONAL_REGISTER hcOp;
|
|
ENDPOINT_TRANSFER_TYPE epType;
|
|
|
|
qh = EndpointData->QueueHead;
|
|
|
|
epType = EndpointData->Parameters.TransferType;
|
|
|
|
switch(State) {
|
|
case ENDPOINT_ACTIVE:
|
|
if (epType == Interrupt) {
|
|
// now insert the qh in the schedule
|
|
EHCI_InsertQueueHeadInPeriodicList(DeviceData,
|
|
EndpointData);
|
|
|
|
} else {
|
|
// put queue head in the schedule
|
|
EHCI_InsertQueueHeadInAsyncList(DeviceData,
|
|
EndpointData->QueueHead);
|
|
}
|
|
break;
|
|
|
|
case ENDPOINT_PAUSE:
|
|
// remove queue head from the schedule
|
|
if (epType == Interrupt) {
|
|
EHCI_RemoveQueueHeadFromPeriodicList(DeviceData,
|
|
EndpointData);
|
|
} else {
|
|
EHCI_RemoveQueueHeadFromAsyncList(DeviceData,
|
|
EndpointData->QueueHead);
|
|
}
|
|
break;
|
|
|
|
case ENDPOINT_REMOVE:
|
|
qh->QhFlags |= EHCI_QH_FLAG_QH_REMOVED;
|
|
|
|
if (epType == Interrupt) {
|
|
EHCI_RemoveQueueHeadFromPeriodicList(DeviceData,
|
|
EndpointData);
|
|
} else {
|
|
EHCI_RemoveQueueHeadFromAsyncList(DeviceData,
|
|
EndpointData->QueueHead);
|
|
}
|
|
|
|
// generate a cache flush after we remove so the the HW
|
|
// does not have the QH cached
|
|
EHCI_InterruptNextSOF(DeviceData);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
TEST_TRAP();
|
|
}
|
|
|
|
EndpointData->State = State;
|
|
}
|
|
|
|
|
|
MP_ENDPOINT_STATUS
|
|
EHCI_GetAsyncEndpointStatus(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
MP_ENDPOINT_STATUS status;
|
|
|
|
status = ENDPOINT_STATUS_RUN;
|
|
|
|
if (TEST_FLAG(EndpointData->Flags, EHCI_EDFLAG_HALTED)) {
|
|
status = ENDPOINT_STATUS_HALT;
|
|
}
|
|
|
|
LOGENTRY(DeviceData, G, '_gps', EndpointData, status, 0);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
VOID
|
|
EHCI_SetAsyncEndpointStatus(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData,
|
|
MP_ENDPOINT_STATUS Status
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PHC_OPERATIONAL_REGISTER hc;
|
|
PHCD_QUEUEHEAD_DESCRIPTOR qh;
|
|
|
|
qh = EndpointData->QueueHead;
|
|
|
|
LOGENTRY(DeviceData, G, '_set', EndpointData, Status, 0);
|
|
|
|
switch(Status) {
|
|
case ENDPOINT_STATUS_RUN:
|
|
CLEAR_FLAG(EndpointData->Flags, EHCI_EDFLAG_HALTED);
|
|
|
|
qh->HwQH.Overlay.qTD.Token.Halted = 0;
|
|
break;
|
|
|
|
case ENDPOINT_STATUS_HALT:
|
|
TEST_TRAP();
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
EHCI_ProcessDoneAsyncTd(
|
|
PDEVICE_DATA DeviceData,
|
|
PHCD_TRANSFER_DESCRIPTOR Td
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
process a completed TD
|
|
|
|
Parameters
|
|
|
|
--*/
|
|
{
|
|
PTRANSFER_CONTEXT transferContext;
|
|
PENDPOINT_DATA endpointData;
|
|
PTRANSFER_PARAMETERS tp;
|
|
USBD_STATUS usbdStatus;
|
|
ULONG byteCount;
|
|
|
|
transferContext = TRANSFER_CONTEXT_PTR(Td->TransferContext);
|
|
ASSERT_TRANSFER(DeviceData, transferContext);
|
|
|
|
tp = transferContext->TransferParameters;
|
|
transferContext->PendingTds--;
|
|
endpointData = transferContext->EndpointData;
|
|
|
|
if (TEST_FLAG(Td->Flags, TD_FLAG_SKIP)) {
|
|
LOGENTRY(DeviceData, G, '_Ktd', transferContext,
|
|
0,
|
|
Td);
|
|
|
|
goto free_it;
|
|
}
|
|
|
|
// completion status for this TD?
|
|
// since the endpoint halts on error the error
|
|
// bits should have been written back to the TD
|
|
// we use these bits to dermine the error
|
|
if (Td->HwTD.Token.Halted == 1) {
|
|
usbdStatus = EHCI_GetErrorFromTD(DeviceData,
|
|
endpointData,
|
|
Td);
|
|
} else {
|
|
usbdStatus = USBD_STATUS_SUCCESS;
|
|
}
|
|
|
|
LOGENTRY(DeviceData, G, '_Dtd', transferContext,
|
|
usbdStatus,
|
|
Td);
|
|
|
|
byteCount = Td->TransferLength -
|
|
Td->HwTD.Token.BytesToTransfer;
|
|
|
|
LOGENTRY(DeviceData, G, '_tln', byteCount,
|
|
Td->TransferLength, Td->HwTD.Token.BytesToTransfer);
|
|
|
|
if (Td->HwTD.Token.Pid != HcTOK_Setup) {
|
|
|
|
// data or status phase of a control transfer or a bulk/int
|
|
// data transfer
|
|
LOGENTRY(DeviceData, G, '_Idt', Td, transferContext, byteCount);
|
|
|
|
transferContext->BytesTransferred += byteCount;
|
|
|
|
}
|
|
|
|
// note that we only set transferContext->UsbdStatus
|
|
// if we find a TD with an error this will cause us to
|
|
// record the last TD with an error as the error for
|
|
// the transfer.
|
|
if (USBD_STATUS_SUCCESS != usbdStatus) {
|
|
|
|
// map the error to code in USBDI.H
|
|
transferContext->UsbdStatus =
|
|
usbdStatus;
|
|
|
|
LOGENTRY(DeviceData, G, '_tER', transferContext->UsbdStatus, 0, 0);
|
|
}
|
|
|
|
free_it:
|
|
|
|
// mark the TD free
|
|
EHCI_FREE_TD(DeviceData, endpointData, Td);
|
|
|
|
if (transferContext->PendingTds == 0) {
|
|
// all TDs for this transfer are done
|
|
// clear the HAVE_TRANSFER flag to indicate
|
|
// we can take another
|
|
endpointData->PendingTransfers--;
|
|
|
|
if (endpointData->Parameters.TransferType == Bulk ||
|
|
endpointData->Parameters.TransferType == Control) {
|
|
|
|
USBCMD cmd;
|
|
PHC_OPERATIONAL_REGISTER hcOp;
|
|
|
|
hcOp = DeviceData->OperationalRegisters;
|
|
DeviceData->PendingControlAndBulk--;
|
|
|
|
cmd.ul = READ_REGISTER_ULONG(&hcOp->UsbCommand.ul);
|
|
|
|
if (DeviceData->PendingControlAndBulk == 0 &&
|
|
cmd.IntOnAsyncAdvanceDoorbell == 0 &&
|
|
TEST_FLAG(DeviceData->Flags, EHCI_DD_EN_IDLE_EP_SUPPORT)) {
|
|
EHCI_DisableAsyncList(DeviceData);
|
|
}
|
|
}
|
|
//if (transferContext->BytesTransferred == 0 &&
|
|
// endpointData->Parameters.TransferType == Bulk) {
|
|
// TEST_TRAP();
|
|
//}
|
|
LOGENTRY(DeviceData, G, '_cpt',
|
|
transferContext->UsbdStatus,
|
|
transferContext,
|
|
transferContext->BytesTransferred);
|
|
|
|
|
|
USBPORT_COMPLETE_TRANSFER(DeviceData,
|
|
endpointData,
|
|
tp,
|
|
transferContext->UsbdStatus,
|
|
transferContext->BytesTransferred);
|
|
}
|
|
}
|
|
|
|
|
|
USBD_STATUS
|
|
EHCI_GetErrorFromTD(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData,
|
|
PHCD_TRANSFER_DESCRIPTOR Td
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Maps the error bits in the TD to a USBD_STATUS code
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
LOGENTRY(DeviceData, G, '_eTD', Td->HwTD.Token.ul, Td, 0);
|
|
|
|
EHCI_ASSERT(DeviceData, Td->HwTD.Token.Halted == 1);
|
|
//ULONG MissedMicroFrame:1; // 2
|
|
//ULONG XactErr:1; // 3
|
|
//ULONG BabbleDetected:1; // 4
|
|
//ULONG DataBufferError:1; // 5
|
|
|
|
if (Td->HwTD.Token.XactErr) {
|
|
LOGENTRY(DeviceData, G, '_mp1', 0, 0, 0);
|
|
|
|
return USBD_STATUS_XACT_ERROR;
|
|
}
|
|
|
|
if (Td->HwTD.Token.BabbleDetected) {
|
|
LOGENTRY(DeviceData, G, '_mp2', 0, 0, 0);
|
|
|
|
return USBD_STATUS_BABBLE_DETECTED;
|
|
}
|
|
|
|
if (Td->HwTD.Token.DataBufferError) {
|
|
LOGENTRY(DeviceData, G, '_mp3', 0, 0, 0);
|
|
|
|
return USBD_STATUS_DATA_BUFFER_ERROR;
|
|
}
|
|
|
|
if (Td->HwTD.Token.MissedMicroFrame) {
|
|
LOGENTRY(DeviceData, G, '_mp6', 0, 0, 0);
|
|
return USBD_STATUS_XACT_ERROR;
|
|
}
|
|
|
|
// no bit set -- treat as a stall
|
|
LOGENTRY(DeviceData, G, '_mp4', 0, 0, 0);
|
|
return USBD_STATUS_STALL_PID;
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
EHCI_AbortAsyncTransfer(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData,
|
|
PTRANSFER_CONTEXT AbortTransferContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called when the endpoint 'needs attention'
|
|
|
|
The goal here is to determine which TDs, if any,
|
|
have completed and complete ant associated transfers
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
|
|
PHCD_TRANSFER_DESCRIPTOR td, currentTd;
|
|
PHCD_QUEUEHEAD_DESCRIPTOR qh;
|
|
HW_32BIT_PHYSICAL_ADDRESS abortTdPhys;
|
|
PTRANSFER_CONTEXT currentTransfer;
|
|
ULONG byteCount;
|
|
|
|
qh = EndpointData->QueueHead;
|
|
|
|
// The endpoint should not be in the schedule
|
|
|
|
LOGENTRY(DeviceData, G, '_abr', qh, AbortTransferContext, EndpointData->HcdHeadP);
|
|
EHCI_ASSERT(DeviceData, !TEST_FLAG(qh->QhFlags, EHCI_QH_FLAG_IN_SCHEDULE));
|
|
|
|
// one less pending transfer
|
|
EndpointData->PendingTransfers--;
|
|
|
|
// our mission now is to remove all TDs associated with
|
|
// this transfer
|
|
|
|
// get the last known head, we update the head when we process
|
|
// (AKA poll) the enopoint.
|
|
// walk the list to the tail (dummy) TD
|
|
|
|
// find the transfer we wish to cancel...
|
|
|
|
// walk the list and find the first TD belonging
|
|
// to this transfer
|
|
|
|
// cases to handle
|
|
// case 1 this is the first transfer in the list
|
|
// case 2 this is a middle transfer in the list
|
|
// case 3 this is the last transfer in the list
|
|
// case 4 transfer is not in the list
|
|
|
|
td = EndpointData->HcdHeadP;
|
|
|
|
ASSERT_TD(DeviceData, td);
|
|
|
|
if (TRANSFER_CONTEXT_PTR(td->TransferContext) == AbortTransferContext) {
|
|
|
|
// case 1
|
|
byteCount = 0;
|
|
|
|
while (td != EndpointData->DummyTd &&
|
|
TRANSFER_CONTEXT_PTR(td->TransferContext) == AbortTransferContext) {
|
|
PHCD_TRANSFER_DESCRIPTOR tmp;
|
|
|
|
// see if any data has been transferred
|
|
byteCount += (td->TransferLength -
|
|
td->HwTD.Token.BytesToTransfer);
|
|
|
|
tmp = td;
|
|
td = TRANSFER_DESCRIPTOR_PTR(td->NextHcdTD);
|
|
EHCI_FREE_TD(DeviceData, EndpointData, tmp)
|
|
}
|
|
|
|
if (byteCount) {
|
|
AbortTransferContext->BytesTransferred += byteCount;
|
|
}
|
|
|
|
// td now points to the 'next transfer TD'
|
|
|
|
// this puts us in the 'advance queue' state
|
|
// ie overlay is !active && !halted, update the
|
|
// overlay area as appropriate.
|
|
//
|
|
// NOTE: the hw is not accessing the qh at this time
|
|
|
|
// do not zero the queue head because this will
|
|
// trash the state of the data toggle
|
|
//RtlZeroMemory(&qh->HwQH.Overlay.qTD,
|
|
// sizeof(qh->HwQH.Overlay.qTD));
|
|
|
|
// point at the waste area to check for sync problems
|
|
qh->HwQH.CurrentTD.HwAddress = EndpointData->QhChkPhys;
|
|
|
|
qh->HwQH.Overlay.qTD.AltNext_qTD.HwAddress =
|
|
td->HwTD.AltNext_qTD.HwAddress;
|
|
qh->HwQH.Overlay.qTD.Next_qTD.HwAddress =
|
|
td->PhysicalAddress;
|
|
qh->HwQH.Overlay.qTD.Token.BytesToTransfer = 0;
|
|
qh->HwQH.Overlay.qTD.Token.Active = 0;
|
|
qh->HwQH.Overlay.qTD.Token.Halted = 0;
|
|
|
|
EndpointData->HcdHeadP = td;
|
|
|
|
} else {
|
|
|
|
PHCD_TRANSFER_DESCRIPTOR prevTd, nextTd;
|
|
|
|
// determine the current transfer in the overlay
|
|
EHCI_ASSERT(DeviceData, qh->HwQH.CurrentTD.HwAddress);
|
|
|
|
currentTd = (PHCD_TRANSFER_DESCRIPTOR)
|
|
USBPORT_PHYSICAL_TO_VIRTUAL(qh->HwQH.CurrentTD.HwAddress,
|
|
DeviceData,
|
|
EndpointData);
|
|
currentTransfer =
|
|
TRANSFER_CONTEXT_PTR(currentTd->TransferContext);
|
|
|
|
LOGENTRY(DeviceData, G, '_Act', currentTransfer,
|
|
currentTd, EndpointData->HcdHeadP);
|
|
|
|
// case 2, 3
|
|
|
|
// walk from the head to the first td of the transfer
|
|
// we are interested in.
|
|
|
|
prevTd = td = EndpointData->HcdHeadP;
|
|
while (td != NULL) {
|
|
PHCD_TRANSFER_DESCRIPTOR tmp;
|
|
|
|
if (TRANSFER_CONTEXT_PTR(td->TransferContext) == AbortTransferContext) {
|
|
break;
|
|
}
|
|
|
|
prevTd = td;
|
|
td = TRANSFER_DESCRIPTOR_PTR(td->NextHcdTD);
|
|
|
|
LOGENTRY(DeviceData, G, '_nxt', prevTd, td, 0);
|
|
|
|
}
|
|
LOGENTRY(DeviceData, G, '_atd', 0, td, 0);
|
|
|
|
abortTdPhys = td->PhysicalAddress;
|
|
// now walk to the first td of the next transfer, free
|
|
// TDs for this transfer as we go
|
|
while (td != NULL &&
|
|
TRANSFER_CONTEXT_PTR(td->TransferContext) == AbortTransferContext) {
|
|
PHCD_TRANSFER_DESCRIPTOR tmp;
|
|
|
|
tmp = td;
|
|
td = TRANSFER_DESCRIPTOR_PTR(td->NextHcdTD);
|
|
EHCI_FREE_TD(DeviceData, EndpointData, tmp);
|
|
}
|
|
|
|
nextTd = td;
|
|
|
|
LOGENTRY(DeviceData, G, '_Apn', prevTd,
|
|
nextTd, abortTdPhys);
|
|
|
|
// now link prevTd to nextTd
|
|
if (prevTd == NULL) {
|
|
// case 4 transfer not in the list
|
|
// should this happen?
|
|
TEST_TRAP();
|
|
}
|
|
|
|
// next TD might be dummy
|
|
EHCI_ASSERT(DeviceData, nextTd != NULL);
|
|
EHCI_ASSERT(DeviceData, prevTd != NULL);
|
|
|
|
//SET_NEXT_AND_ALTNEXT_TD
|
|
SET_NEXT_TD(DeviceData, prevTd, nextTd);
|
|
SET_ALTNEXT_TD(DeviceData, prevTd, nextTd);
|
|
|
|
// fixup overlay area as needed,
|
|
// if the aborted transfer was current we want to pick
|
|
// up the next transfer
|
|
|
|
if (currentTransfer == AbortTransferContext) {
|
|
LOGENTRY(DeviceData, G, '_At1', currentTransfer, 0, 0);
|
|
// aborted transfer is current, prime the
|
|
// overlay with the next transfer
|
|
|
|
// catch HW sync problems
|
|
qh->HwQH.CurrentTD.HwAddress = EndpointData->QhChkPhys;
|
|
|
|
qh->HwQH.Overlay.qTD.Next_qTD.HwAddress = nextTd->PhysicalAddress;
|
|
qh->HwQH.Overlay.qTD.AltNext_qTD.HwAddress = EHCI_TERMINATE_BIT;
|
|
qh->HwQH.Overlay.qTD.Token.BytesToTransfer = 0;
|
|
qh->HwQH.Overlay.qTD.Token.Active = 0;
|
|
// preserve halted bit
|
|
|
|
} else if (TRANSFER_CONTEXT_PTR(prevTd->TransferContext) ==
|
|
currentTransfer) {
|
|
// previous transfer was current, make sure the overlay
|
|
// area (current transfer) does not point to a deleted td
|
|
LOGENTRY(DeviceData, G, '_At2', currentTransfer, 0, 0);
|
|
|
|
// check overlay
|
|
if (qh->HwQH.Overlay.qTD.Next_qTD.HwAddress == abortTdPhys) {
|
|
qh->HwQH.Overlay.qTD.Next_qTD.HwAddress =
|
|
nextTd->PhysicalAddress;
|
|
}
|
|
|
|
if (qh->HwQH.Overlay.qTD.AltNext_qTD.HwAddress == abortTdPhys) {
|
|
qh->HwQH.Overlay.qTD.AltNext_qTD.HwAddress =
|
|
nextTd->PhysicalAddress;
|
|
}
|
|
|
|
// correct all TDs for the current transfer
|
|
td = EndpointData->HcdHeadP;
|
|
while (td != NULL) {
|
|
|
|
// nextTd is firstTd of the NEXT transfer
|
|
if (TRANSFER_CONTEXT_PTR(td->TransferContext) == currentTransfer) {
|
|
// alt next always points to next transfer
|
|
td->HwTD.AltNext_qTD.HwAddress = nextTd->PhysicalAddress;
|
|
SET_ALTNEXT_TD(DeviceData, td, nextTd);
|
|
}
|
|
|
|
td = TRANSFER_DESCRIPTOR_PTR(td->NextHcdTD);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
USB_MINIPORT_STATUS
|
|
EHCI_PokeAsyncEndpoint(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_PARAMETERS EndpointParameters,
|
|
PENDPOINT_DATA EndpointData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PHCD_QUEUEHEAD_DESCRIPTOR qh;
|
|
|
|
qh = EndpointData->QueueHead;
|
|
EHCI_ASSERT(DeviceData, qh != NULL);
|
|
|
|
EndpointData->Parameters = *EndpointParameters;
|
|
|
|
qh->HwQH.EpChars.DeviceAddress =
|
|
EndpointData->Parameters.DeviceAddress;
|
|
|
|
qh->HwQH.EpChars.MaximumPacketLength =
|
|
EndpointData->Parameters.MaxPacketSize;
|
|
|
|
qh->HwQH.EpCaps.HubAddress =
|
|
EndpointData->Parameters.TtDeviceAddress;
|
|
|
|
return USBMP_STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
VOID
|
|
EHCI_LockQueueHead(
|
|
PDEVICE_DATA DeviceData,
|
|
PHCD_QUEUEHEAD_DESCRIPTOR Qh,
|
|
ENDPOINT_TRANSFER_TYPE EpType
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Synchronously update the overlate area, this involves using the
|
|
doorbell to wait for queue head to flush off the HC hardware
|
|
|
|
the caller is responisble for resuming
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PHC_OPERATIONAL_REGISTER hcOp;
|
|
USBCMD cmd;
|
|
PHCD_QUEUEHEAD_DESCRIPTOR nextQh, prevQh;
|
|
HW_32BIT_PHYSICAL_ADDRESS phys;
|
|
ULONG mf, cmf;
|
|
|
|
hcOp = DeviceData->OperationalRegisters;
|
|
|
|
LOGENTRY(DeviceData, G, '_LKq', Qh, 0, 0);
|
|
|
|
EHCI_ASSERT(DeviceData, !TEST_FLAG(Qh->QhFlags, EHCI_QH_FLAG_UPDATING));
|
|
EHCI_ASSERT(DeviceData, DeviceData->LockQh == NULL);
|
|
|
|
SET_FLAG(Qh->QhFlags, EHCI_QH_FLAG_UPDATING);
|
|
|
|
nextQh = QH_DESCRIPTOR_PTR(Qh->NextQh);
|
|
prevQh = QH_DESCRIPTOR_PTR(Qh->PrevQh);
|
|
ASSERT(prevQh);
|
|
|
|
DeviceData->LockPrevQh = prevQh;
|
|
DeviceData->LockNextQh = nextQh;
|
|
DeviceData->LockQh = Qh;
|
|
|
|
if (nextQh) {
|
|
phys = nextQh->PhysicalAddress;
|
|
SET_QH(phys);
|
|
} else {
|
|
phys = 0;
|
|
SET_T_BIT(phys);
|
|
}
|
|
|
|
// note that we only mess with the HW nextlinks and this
|
|
// is temporary
|
|
|
|
// unlink this queue head
|
|
prevQh->HwQH.HLink.HwAddress = phys;
|
|
mf = READ_REGISTER_ULONG(&hcOp->UsbFrameIndex.ul);
|
|
|
|
if (EpType == Interrupt) {
|
|
|
|
do {
|
|
cmf = READ_REGISTER_ULONG(&hcOp->UsbFrameIndex.ul);
|
|
} while (cmf == mf);
|
|
|
|
} else {
|
|
EHCI_AsyncCacheFlush(DeviceData);
|
|
}
|
|
|
|
LOGENTRY(DeviceData, G, '_LKx', Qh, 0, 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
EHCI_AsyncCacheFlush(
|
|
PDEVICE_DATA DeviceData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Synchronously flushes the async controller cache by ringing
|
|
the async doorbell and waiting
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PHC_OPERATIONAL_REGISTER hcOp;
|
|
USBCMD cmd;
|
|
USBSTS sts;
|
|
|
|
hcOp = DeviceData->OperationalRegisters;
|
|
|
|
cmd.ul = READ_REGISTER_ULONG(&hcOp->UsbCommand.ul);
|
|
sts.ul = READ_REGISTER_ULONG(&hcOp->UsbStatus.ul);
|
|
|
|
// check the real status of the async list. if disabled
|
|
// we should not need to flush the cache.
|
|
// 0
|
|
if (sts.AsyncScheduleStatus == 0 &&
|
|
cmd.AsyncScheduleEnable == 0) {
|
|
return;
|
|
}
|
|
|
|
// 1->0 wait for it to go to 0
|
|
if (sts.AsyncScheduleStatus == 1 &&
|
|
cmd.AsyncScheduleEnable == 0) {
|
|
do {
|
|
sts.ul = READ_REGISTER_ULONG(&hcOp->UsbStatus.ul);
|
|
cmd.ul = READ_REGISTER_ULONG(&hcOp->UsbCommand.ul);
|
|
} while (sts.AsyncScheduleStatus &&
|
|
cmd.ul != 0xFFFFFFFF &&
|
|
cmd.HostControllerRun);
|
|
return;
|
|
}
|
|
|
|
// 0->1 wait for it to enable
|
|
if (sts.AsyncScheduleStatus == 0 &&
|
|
cmd.AsyncScheduleEnable == 1) {
|
|
do {
|
|
sts.ul = READ_REGISTER_ULONG(&hcOp->UsbStatus.ul);
|
|
cmd.ul = READ_REGISTER_ULONG(&hcOp->UsbCommand.ul);
|
|
} while (!sts.AsyncScheduleStatus &&
|
|
cmd.ul != 0xFFFFFFFF &&
|
|
cmd.HostControllerRun);
|
|
}
|
|
|
|
EHCI_ASSERT(DeviceData, cmd.AsyncScheduleEnable == 1);
|
|
EHCI_ASSERT(DeviceData, sts.AsyncScheduleStatus == 1);
|
|
// if not enabled enable it, this would be a bug though
|
|
// cmd.AsyncScheduleEnable = 1;
|
|
|
|
// cmd.IntOnAsyncAdvanceDoorbell = 1;
|
|
// WRITE_REGISTER_ULONG(&hcOp->UsbCommand.ul,
|
|
// cmd.ul);
|
|
|
|
// wait for it.
|
|
cmd.ul = READ_REGISTER_ULONG(&hcOp->UsbCommand.ul);
|
|
while (cmd.IntOnAsyncAdvanceDoorbell &&
|
|
cmd.HostControllerRun &&
|
|
cmd.ul != 0xFFFFFFFF) {
|
|
KeStallExecutionProcessor(1);
|
|
cmd.ul = READ_REGISTER_ULONG(&hcOp->UsbCommand.ul);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
EHCI_UnlockQueueHead(
|
|
PDEVICE_DATA DeviceData,
|
|
PHCD_QUEUEHEAD_DESCRIPTOR Qh
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
compliment to LockQueueHead, this function reactivates the qh after
|
|
modifications are complete
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PHCD_QUEUEHEAD_DESCRIPTOR nextQh, prevQh;
|
|
HW_32BIT_PHYSICAL_ADDRESS phys;
|
|
|
|
LOGENTRY(DeviceData, G, '_UKq', Qh, 0, 0);
|
|
EHCI_ASSERT(DeviceData, TEST_FLAG(Qh->QhFlags, EHCI_QH_FLAG_UPDATING));
|
|
EHCI_ASSERT(DeviceData, DeviceData->LockQh != NULL);
|
|
EHCI_ASSERT(DeviceData, DeviceData->LockQh == Qh);
|
|
|
|
CLEAR_FLAG(Qh->QhFlags, EHCI_QH_FLAG_UPDATING);
|
|
DeviceData->LockQh = NULL;
|
|
|
|
prevQh = DeviceData->LockPrevQh;
|
|
|
|
phys = Qh->PhysicalAddress;
|
|
SET_QH(phys);
|
|
|
|
prevQh->HwQH.HLink.HwAddress = phys;
|
|
|
|
LOGENTRY(DeviceData, G, '_UKx', Qh, 0, phys);
|
|
}
|
|
|
|
|
|
VOID
|
|
EHCI_PollActiveAsyncEndpoint(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The queue head is in the running state we will just process
|
|
the TDs that are completed up to 'current' if dummy goes
|
|
current then all TDs will be complete
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
PHCD_TRANSFER_DESCRIPTOR td, currentTd;
|
|
PHCD_QUEUEHEAD_DESCRIPTOR qh;
|
|
HW_32BIT_PHYSICAL_ADDRESS tdPhys, curTdPhys;
|
|
PTRANSFER_CONTEXT transfer;
|
|
ULONG cf = 0;
|
|
BOOLEAN syncWithHw;
|
|
|
|
#if DBG
|
|
cf = EHCI_Get32BitFrameNumber(DeviceData);
|
|
#endif
|
|
|
|
qh = EndpointData->QueueHead;
|
|
curTdPhys = qh->HwQH.CurrentTD.HwAddress & ~HW_LINK_FLAGS_MASK;
|
|
|
|
LOGENTRY(DeviceData, G, '_pol', qh, cf, curTdPhys);
|
|
|
|
EHCI_ASSERT(DeviceData, curTdPhys != 0);
|
|
currentTd = (PHCD_TRANSFER_DESCRIPTOR)
|
|
USBPORT_PHYSICAL_TO_VIRTUAL(curTdPhys,
|
|
DeviceData,
|
|
EndpointData);
|
|
|
|
// walk the soft list of TDs and complete all TDs
|
|
// up to the currentTD
|
|
|
|
// get the last known head
|
|
LOGENTRY(DeviceData, G, '_hd1',
|
|
EndpointData->HcdHeadP,
|
|
0,
|
|
currentTd);
|
|
|
|
if (currentTd == EndpointData->QhChk) {
|
|
// endpoint is transitioning to run a transfer or
|
|
// is pointing at the waste area, do not poll at
|
|
// this time
|
|
LOGENTRY(DeviceData, G, '_pl!', 0, 0, currentTd);
|
|
return;
|
|
}
|
|
|
|
// only do HW sync if QH is not in schedule
|
|
syncWithHw = TEST_FLAG(qh->QhFlags, EHCI_QH_FLAG_IN_SCHEDULE) ?
|
|
TRUE : FALSE;
|
|
// skip sync on hot remove
|
|
if (EHCI_HardwarePresent(DeviceData, FALSE) == FALSE) {
|
|
syncWithHw = FALSE;
|
|
}
|
|
|
|
ASSERT_TD(DeviceData, currentTd);
|
|
td = EndpointData->HcdHeadP;
|
|
|
|
if (td == currentTd &&
|
|
td != EndpointData->DummyTd) {
|
|
// currentTd is head verify that it is not complete
|
|
if (td->HwTD.Token.Active == 0) {
|
|
PHCD_TRANSFER_DESCRIPTOR tmp;
|
|
|
|
//currentTd = TRANSFER_DESCRIPTOR_PTR(td->NextHcdTD);
|
|
LOGENTRY(DeviceData, G, '_cAT', td, currentTd,
|
|
qh->HwQH.CurrentTD.HwAddress & ~HW_LINK_FLAGS_MASK);
|
|
|
|
tmp = TRANSFER_DESCRIPTOR_PTR(td->NextHcdTD);
|
|
if (tmp &&
|
|
td->HwTD.Next_qTD.HwAddress != tmp->PhysicalAddress) {
|
|
td->HwTD.Next_qTD.HwAddress = tmp->PhysicalAddress;
|
|
}
|
|
|
|
tmp = TRANSFER_DESCRIPTOR_PTR(td->AltNextHcdTD);
|
|
if (tmp &&
|
|
td->HwTD.AltNext_qTD.HwAddress != tmp->PhysicalAddress) {
|
|
td->HwTD.AltNext_qTD.HwAddress = tmp->PhysicalAddress;
|
|
}
|
|
|
|
if (qh->HwQH.CurrentTD.HwAddress == td->PhysicalAddress &&
|
|
td->HwTD.Token.Active == 0 &&
|
|
(qh->HwQH.Overlay.qTD.Next_qTD.HwAddress !=
|
|
td->HwTD.Next_qTD.HwAddress ||
|
|
qh->HwQH.Overlay.qTD.AltNext_qTD.HwAddress !=
|
|
td->HwTD.AltNext_qTD.HwAddress)) {
|
|
|
|
LOGENTRY(DeviceData, G, '_upp', qh, td, 0);
|
|
|
|
qh->HwQH.Overlay.qTD.Next_qTD.HwAddress =
|
|
td->HwTD.Next_qTD.HwAddress;
|
|
qh->HwQH.Overlay.qTD.AltNext_qTD.HwAddress =
|
|
td->HwTD.AltNext_qTD.HwAddress;
|
|
|
|
}
|
|
|
|
EHCI_InterruptNextSOF(DeviceData);
|
|
}
|
|
}
|
|
|
|
while (td != currentTd) {
|
|
|
|
EHCI_ASSERT(DeviceData, !TEST_FLAG(td->Flags, TD_FLAG_DUMMY));
|
|
// TDs between head and current should not be active
|
|
|
|
transfer = TRANSFER_CONTEXT_PTR(td->TransferContext);
|
|
LOGENTRY(DeviceData, G, '_dt1', td, 0, transfer);
|
|
if (td->HwTD.Token.Active == 1) {
|
|
// if the TD is active then it must have been
|
|
// skipped due to a short xfer condition
|
|
LOGENTRY(DeviceData, G, '_dtS', td, 0, 0);
|
|
SET_FLAG(td->Flags, TD_FLAG_SKIP);
|
|
}
|
|
|
|
SET_FLAG(td->Flags, TD_FLAG_DONE);
|
|
|
|
InsertTailList(&EndpointData->DoneTdList,
|
|
&td->DoneLink);
|
|
|
|
td = TRANSFER_DESCRIPTOR_PTR(td->NextHcdTD);
|
|
}
|
|
|
|
// now check current TD if next td is the dummy and this td is
|
|
// not active then we need to bump the current TD to dummy and
|
|
// complete this TD. This will only occur if this is the last TD
|
|
// queued
|
|
// also check if this is a short transfer on the last transfer queued,
|
|
// in this case the AltNextHcdTd will point to dummy and we will need
|
|
// to advance passed the skipped TDs.
|
|
|
|
|
|
if ((TRANSFER_DESCRIPTOR_PTR(currentTd->NextHcdTD) ==
|
|
EndpointData->DummyTd &&
|
|
currentTd->HwTD.Token.Active == 0) ||
|
|
// or a short packet
|
|
(TRANSFER_DESCRIPTOR_PTR(currentTd->AltNextHcdTD) ==
|
|
EndpointData->DummyTd &&
|
|
currentTd->HwTD.Token.Active == 0 &&
|
|
currentTd->HwTD.Token.BytesToTransfer != 0) ) {
|
|
|
|
LOGENTRY(DeviceData, G, '_bmp', currentTd, 0, 0);
|
|
// synchronize with hardware in the event this td
|
|
// has not been completely written back
|
|
|
|
// since we are about to trash the overlay area there should
|
|
// be no transfer current, we use the async doorbell to wait
|
|
// for an the async TD to be completely flushed.
|
|
//
|
|
// In the event of a periodic transfer the HW may have prefetched
|
|
// the periodic list so we need to wait for the microframe counter
|
|
// to turn over.
|
|
|
|
if (syncWithHw) {
|
|
EHCI_LockQueueHead(DeviceData,
|
|
qh,
|
|
EndpointData->Parameters.TransferType);
|
|
}
|
|
|
|
qh->HwQH.CurrentTD.HwAddress = EndpointData->QhChkPhys;
|
|
|
|
td = currentTd;
|
|
SET_FLAG(td->Flags, TD_FLAG_DONE);
|
|
|
|
InsertTailList(&EndpointData->DoneTdList,
|
|
&td->DoneLink);
|
|
|
|
if (td->HwTD.Token.BytesToTransfer != 0 &&
|
|
TRANSFER_DESCRIPTOR_PTR(td->AltNextHcdTD) == EndpointData->DummyTd) {
|
|
// start at first alt TD
|
|
td = TRANSFER_DESCRIPTOR_PTR(td->NextHcdTD);
|
|
|
|
// short xfer
|
|
while (td != EndpointData->DummyTd) {
|
|
SET_FLAG(td->Flags, TD_FLAG_SKIP);
|
|
InsertTailList(&EndpointData->DoneTdList,
|
|
&td->DoneLink);
|
|
|
|
td = TRANSFER_DESCRIPTOR_PTR(td->NextHcdTD);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
qh->HwQH.CurrentTD.HwAddress = EndpointData->DummyTd->PhysicalAddress;
|
|
qh->HwQH.Overlay.qTD.Next_qTD.HwAddress = EHCI_TERMINATE_BIT;
|
|
qh->HwQH.Overlay.qTD.AltNext_qTD.HwAddress = EHCI_TERMINATE_BIT;
|
|
qh->HwQH.Overlay.qTD.Token.BytesToTransfer = 0;
|
|
|
|
EndpointData->HcdHeadP = EndpointData->DummyTd;
|
|
|
|
if (syncWithHw) {
|
|
EHCI_UnlockQueueHead(DeviceData,
|
|
qh);
|
|
}
|
|
|
|
// check for sync problems
|
|
EHCI_QHCHK(DeviceData, EndpointData);
|
|
|
|
} else {
|
|
|
|
EHCI_ASSERT(DeviceData, td != NULL);
|
|
EndpointData->HcdHeadP = td;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
EHCI_PollHaltedAsyncEndpoint(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
PHCD_TRANSFER_DESCRIPTOR td, currentTd;
|
|
PHCD_QUEUEHEAD_DESCRIPTOR qh;
|
|
HW_QUEUE_ELEMENT_TD overlay;
|
|
HW_32BIT_PHYSICAL_ADDRESS tdPhys, curTdPhys;
|
|
PTRANSFER_CONTEXT transfer, errTransfer;
|
|
BOOLEAN syncWithHw;
|
|
|
|
// we are halted probably due to an error and
|
|
// currentTd should be the offending TD
|
|
qh = EndpointData->QueueHead;
|
|
curTdPhys = qh->HwQH.CurrentTD.HwAddress & ~HW_LINK_FLAGS_MASK;
|
|
|
|
LOGENTRY(DeviceData, G, '_plH', qh, 0, curTdPhys);
|
|
EHCI_ASSERT(DeviceData, curTdPhys != 0);
|
|
|
|
// only do HW sync if QH is not in schedule
|
|
syncWithHw = TEST_FLAG(qh->QhFlags, EHCI_QH_FLAG_IN_SCHEDULE) ?
|
|
TRUE : FALSE;
|
|
// skip sync on hot remove
|
|
if (EHCI_HardwarePresent(DeviceData, FALSE) == FALSE) {
|
|
syncWithHw = FALSE;
|
|
}
|
|
|
|
currentTd = (PHCD_TRANSFER_DESCRIPTOR)
|
|
USBPORT_PHYSICAL_TO_VIRTUAL(curTdPhys,
|
|
DeviceData,
|
|
EndpointData);
|
|
|
|
if (currentTd == EndpointData->QhChk) {
|
|
// endpoint is transitioning to run a transfer or
|
|
// is pointing at the waste area, do not poll at
|
|
// this time
|
|
LOGENTRY(DeviceData, G, '_hl!', 0, 0, currentTd);
|
|
return;
|
|
}
|
|
|
|
ASSERT_TD(DeviceData, currentTd);
|
|
|
|
// we are halted probably due to an error and
|
|
// currentTd should be the offending TD
|
|
// we should not error on the dummy TD
|
|
EHCI_ASSERT(DeviceData, EndpointData->DummyTd != currentTd);
|
|
|
|
|
|
// walk the soft list of TDs and complete all TDs
|
|
// up to the currentTD
|
|
td = EndpointData->HcdHeadP;
|
|
LOGENTRY(DeviceData, G, '_hed', 0, 0, td);
|
|
|
|
while (td != currentTd) {
|
|
|
|
EHCI_ASSERT(DeviceData, !TEST_FLAG(td->Flags, TD_FLAG_DUMMY));
|
|
// TDs between head and current should not be active
|
|
|
|
transfer = TRANSFER_CONTEXT_PTR(td->TransferContext);
|
|
LOGENTRY(DeviceData, G, '_dt2', td, 0, transfer);
|
|
if (td->HwTD.Token.Active == 1) {
|
|
// if the TD is active then it must have been
|
|
// skipped due to a short xfer condition
|
|
LOGENTRY(DeviceData, G, '_d2S', td, 0, 0);
|
|
SET_FLAG(td->Flags, TD_FLAG_SKIP);
|
|
}
|
|
|
|
SET_FLAG(td->Flags, TD_FLAG_DONE);
|
|
|
|
InsertTailList(&EndpointData->DoneTdList,
|
|
&td->DoneLink);
|
|
|
|
td = TRANSFER_DESCRIPTOR_PTR(td->NextHcdTD);
|
|
}
|
|
|
|
// adjust 'currentTd' to be the first TD of the NEXT
|
|
// transfer
|
|
td = currentTd;
|
|
errTransfer = TRANSFER_CONTEXT_PTR(td->TransferContext);
|
|
|
|
while (TRANSFER_CONTEXT_PTR(td->TransferContext) == errTransfer) {
|
|
|
|
LOGENTRY(DeviceData, G, '_d3D', td, 0, 0);
|
|
if (td->HwTD.Token.Active == 1) {
|
|
// if the TD is active then it must have been
|
|
// skipped due to a short xfer condition
|
|
LOGENTRY(DeviceData, G, '_d3S', td, 0, 0);
|
|
SET_FLAG(td->Flags, TD_FLAG_SKIP);
|
|
}
|
|
|
|
SET_FLAG(td->Flags, TD_FLAG_DONE);
|
|
|
|
InsertTailList(&EndpointData->DoneTdList,
|
|
&td->DoneLink);
|
|
|
|
td = TRANSFER_DESCRIPTOR_PTR(td->NextHcdTD);
|
|
|
|
}
|
|
|
|
EHCI_ASSERT(DeviceData, td != NULL);
|
|
// td is now first td of next transfer
|
|
EndpointData->HcdHeadP = currentTd = td;
|
|
|
|
// now fix up the queue head overlay area such that the
|
|
// next transfer will run
|
|
|
|
if (syncWithHw) {
|
|
// sync with the HC hardware
|
|
EHCI_LockQueueHead(DeviceData,
|
|
qh,
|
|
EndpointData->Parameters.TransferType);
|
|
}
|
|
|
|
qh->HwQH.CurrentTD.HwAddress = EndpointData->QhChkPhys;
|
|
|
|
EHCI_ASSERT(DeviceData, qh->HwQH.Overlay.qTD.Token.Halted);
|
|
|
|
// currentTD value should be irrelevent
|
|
// we are !active, halted
|
|
// overlay should be !active !halted when the queue head is reset
|
|
// ie Advance Queue state
|
|
qh->HwQH.Overlay.qTD.Next_qTD.HwAddress = td->PhysicalAddress;
|
|
qh->HwQH.Overlay.qTD.AltNext_qTD.HwAddress = EHCI_TERMINATE_BIT;
|
|
qh->HwQH.Overlay.qTD.Token.BytesToTransfer = 0;
|
|
|
|
if (syncWithHw) {
|
|
// queue head can now be aceesed by HW
|
|
EHCI_UnlockQueueHead(DeviceData,
|
|
qh);
|
|
}
|
|
|
|
// if this is a control endpoint the we need to clear the
|
|
// halt condition
|
|
if (TEST_FLAG(EndpointData->Flags, EHCI_EDFLAG_NOHALT)) {
|
|
LOGENTRY(DeviceData, G, '_clH', qh, 0, 0);
|
|
|
|
CLEAR_FLAG(EndpointData->Flags, EHCI_EDFLAG_HALTED);
|
|
|
|
qh->HwQH.Overlay.qTD.Token.Active = 0;
|
|
qh->HwQH.Overlay.qTD.Token.Halted = 0;
|
|
qh->HwQH.Overlay.qTD.Token.ErrorCounter = 0;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
EHCI_PollAsyncEndpoint(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called when the endpoint 'needs attention'
|
|
|
|
This is where we poll bulk and interrupt endpoins. BI endpoints
|
|
use a 'dummy' TD to denote the end of the current transfer
|
|
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
PHCD_QUEUEHEAD_DESCRIPTOR qh;
|
|
HW_QUEUE_ELEMENT_TD overlay;
|
|
BOOLEAN active, halted;
|
|
PHCD_TRANSFER_DESCRIPTOR td;
|
|
PLIST_ENTRY listEntry;
|
|
ULONG cf = 0;
|
|
|
|
EHCI_QHCHK(DeviceData, EndpointData);
|
|
|
|
#if DBG
|
|
cf = EHCI_Get32BitFrameNumber(DeviceData);
|
|
#endif
|
|
|
|
if (EndpointData->PendingTransfers == 0) {
|
|
// if we have no transfers queued then there is
|
|
// nothing to do
|
|
LOGENTRY(DeviceData, G, '_poN', EndpointData, 0, cf);
|
|
return;
|
|
}
|
|
|
|
|
|
// get the queue head and a snapshot of the overlay
|
|
qh = EndpointData->QueueHead;
|
|
RtlCopyMemory(&overlay,
|
|
&qh->HwQH.Overlay.qTD,
|
|
sizeof(overlay));
|
|
|
|
if (TEST_FLAG(qh->QhFlags, EHCI_QH_FLAG_QH_REMOVED)) {
|
|
// don't check endpoint if qh has been removed
|
|
LOGENTRY(DeviceData, G, '_qRM', EndpointData, 0, cf);
|
|
return;
|
|
}
|
|
|
|
LOGENTRY(DeviceData, G, '_poo', EndpointData, 0, cf);
|
|
|
|
//
|
|
// Active AND Halted -- should never happen
|
|
// !Active AND !Halted -- advance queue head
|
|
// Active AND !Halted -- executing transaction in overlay
|
|
// !Active AND Halted -- queue had is stopped due to an error
|
|
|
|
halted = (BOOLEAN) overlay.Token.Halted;
|
|
active = (BOOLEAN) overlay.Token.Active;
|
|
|
|
if (!active && halted) {
|
|
// queue is halted
|
|
SET_FLAG(EndpointData->Flags, EHCI_EDFLAG_HALTED);
|
|
EHCI_PollHaltedAsyncEndpoint(DeviceData, EndpointData);
|
|
} else {
|
|
// queue is active
|
|
EHCI_PollActiveAsyncEndpoint(DeviceData, EndpointData);
|
|
}
|
|
|
|
// now flush all completed TDs in order of completion from
|
|
// our 'done' List
|
|
|
|
while (!IsListEmpty(&EndpointData->DoneTdList)) {
|
|
|
|
listEntry = RemoveHeadList(&EndpointData->DoneTdList);
|
|
|
|
|
|
td = (PHCD_TRANSFER_DESCRIPTOR) CONTAINING_RECORD(
|
|
listEntry,
|
|
struct _HCD_TRANSFER_DESCRIPTOR,
|
|
DoneLink);
|
|
|
|
|
|
|
|
EHCI_ASSERT(DeviceData, (td->Flags & (TD_FLAG_XFER | TD_FLAG_DONE)));
|
|
EHCI_ProcessDoneAsyncTd(DeviceData,
|
|
td);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
EHCI_AssertQhChk(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData
|
|
)
|
|
{
|
|
PULONG p;
|
|
ULONG i;
|
|
|
|
p = (PULONG) EndpointData->QhChk;
|
|
|
|
for (i=0; i<256/sizeof(*p); i++) {
|
|
EHCI_ASSERT(DeviceData, *p == 0);
|
|
p++;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
EHCI_SetNextTd(
|
|
PDEVICE_DATA DeviceData,
|
|
PHCD_TRANSFER_DESCRIPTOR LinkTd,
|
|
PHCD_TRANSFER_DESCRIPTOR NextTd,
|
|
BOOLEAN SetAltNext
|
|
)
|
|
{
|
|
EHCI_ASSERT(DeviceData, LinkTd != NextTd);\
|
|
|
|
if (SetAltNext) {
|
|
|
|
do {
|
|
LinkTd->HwTD.Next_qTD.HwAddress = NextTd->PhysicalAddress;
|
|
LinkTd->HwTD.AltNext_qTD.HwAddress = NextTd->PhysicalAddress;
|
|
} while (LinkTd->HwTD.Next_qTD.HwAddress !=
|
|
LinkTd->HwTD.AltNext_qTD.HwAddress);
|
|
TRANSFER_DESCRIPTOR_PTR(LinkTd->NextHcdTD) = NextTd;
|
|
TRANSFER_DESCRIPTOR_PTR(LinkTd->AltNextHcdTD) = NextTd;
|
|
|
|
} else {
|
|
LinkTd->HwTD.Next_qTD.HwAddress = NextTd->PhysicalAddress;
|
|
TRANSFER_DESCRIPTOR_PTR(LinkTd->NextHcdTD) = NextTd;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
EHCI_SetAltNextTd(
|
|
PDEVICE_DATA DeviceData,
|
|
PHCD_TRANSFER_DESCRIPTOR LinkTd,
|
|
PHCD_TRANSFER_DESCRIPTOR NextTd
|
|
)
|
|
{
|
|
EHCI_ASSERT(DeviceData, LinkTd != NextTd);
|
|
|
|
LinkTd->HwTD.AltNext_qTD.HwAddress = NextTd->PhysicalAddress;
|
|
|
|
TRANSFER_DESCRIPTOR_PTR(LinkTd->AltNextHcdTD) = NextTd;
|
|
}
|
|
|