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.
1411 lines
44 KiB
1411 lines
44 KiB
/*++
|
|
|
|
Copyright (c) 1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
async.c
|
|
|
|
Abstract:
|
|
|
|
miniport transfer code for control, interrupt 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 Microsoft Corporation. All Rights Reserved.
|
|
|
|
|
|
Revision History:
|
|
|
|
6-26-99 : created, jdunn
|
|
|
|
--*/
|
|
|
|
#include "common.h"
|
|
|
|
//implements the following miniport functions:
|
|
|
|
//non paged
|
|
//OHCI_OpenControlEndpoint
|
|
//OHCI_InterruptTransfer
|
|
//OHCI_OpenControlEndpoint
|
|
|
|
|
|
USB_MINIPORT_STATUS
|
|
OHCI_ControlTransfer(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData,
|
|
PTRANSFER_PARAMETERS TransferParameters,
|
|
PTRANSFER_CONTEXT TransferContext,
|
|
PTRANSFER_SG_LIST TransferSGList
|
|
)
|
|
{
|
|
PHCD_TRANSFER_DESCRIPTOR lastTd, td;
|
|
ULONG lengthMapped, dataTDCount = 0;
|
|
ULONG toggleForDataPhase = HcTDToggle_Data1;
|
|
|
|
// see if we can handle this transfer (put it on the HW)
|
|
// if not return BUSY, port driver will retry later
|
|
|
|
ASSERT_TRANSFER(DeviceData, TransferContext);
|
|
|
|
// NOTE: we can gate the number of transfers
|
|
// by a number of methods:
|
|
// - fixed count
|
|
// - available TDs
|
|
// - registry key
|
|
|
|
// bugbug fixed to one transfer at a time for now
|
|
|
|
//if (EndpointData->PendingTransfers ==
|
|
// EndpointData->MaxPendingTransfers) {
|
|
// TEST_TRAP();
|
|
// return USBMP_STATUS_BUSY;
|
|
//}
|
|
|
|
// Need one TD for every page of the data buffer, plus one for the SETUP
|
|
// TD and one for the STATUS TD.
|
|
//
|
|
if (TransferSGList->SgCount + 2 >
|
|
OHCI_FreeTds(DeviceData, EndpointData)) {
|
|
// not enough TDs!
|
|
return USBMP_STATUS_BUSY;
|
|
}
|
|
|
|
EndpointData->PendingTransfers++;
|
|
|
|
// we have enough tds, program the transfer
|
|
|
|
//
|
|
// first prepare a TD for the setup packet
|
|
//
|
|
|
|
LOGENTRY(DeviceData, G, '_CTR', EndpointData, TransferParameters, 0);
|
|
|
|
//
|
|
// grab the dummy TD from the tail of the queue
|
|
//
|
|
lastTd = td = EndpointData->HcdTailP;
|
|
OHCI_ASSERT(DeviceData, td->Flags & TD_FLAG_BUSY);
|
|
INITIALIZE_TD_FOR_TRANSFER(td, TransferContext);
|
|
// count setup TD
|
|
TransferContext->PendingTds++;
|
|
|
|
//
|
|
// Move setup data into TD (8 chars long)
|
|
//
|
|
RtlCopyMemory(&td->HwTD.Packet[0],
|
|
&TransferParameters->SetupPacket[0],
|
|
8);
|
|
|
|
td->HwTD.CBP = (ULONG)(((PCHAR) & td->HwTD.Packet[0])
|
|
- ((PCHAR) &td->HwTD)) + td->PhysicalAddress;
|
|
td->HwTD.BE = td->HwTD.CBP + 7;
|
|
td->HwTD.Control = 0;
|
|
|
|
td->HwTD.Asy.Direction = HcTDDirection_Setup;
|
|
td->HwTD.Asy.IntDelay = HcTDIntDelay_NoInterrupt;
|
|
td->HwTD.Asy.Toggle = HcTDToggle_Data0;
|
|
td->HwTD.Asy.ConditionCode = HcCC_NotAccessed;
|
|
|
|
|
|
LOGENTRY(DeviceData,
|
|
G, '_set',
|
|
td,
|
|
*((PLONG) &TransferParameters->SetupPacket[0]),
|
|
*((PLONG) &TransferParameters->SetupPacket[4]));
|
|
|
|
// allocate another TD
|
|
lastTd = td;
|
|
td = OHCI_ALLOC_TD(DeviceData, EndpointData);
|
|
OHCI_ASSERT(DeviceData, td != USB_BAD_PTR);
|
|
INITIALIZE_TD_FOR_TRANSFER(td, TransferContext);
|
|
SET_NEXT_TD(lastTd, td);
|
|
|
|
//
|
|
// now setup the data phase
|
|
//
|
|
|
|
lengthMapped = 0;
|
|
while (lengthMapped < TransferParameters->TransferBufferLength) {
|
|
|
|
//
|
|
// fields for data TD
|
|
//
|
|
|
|
dataTDCount++;
|
|
// count this Data TD
|
|
TransferContext->PendingTds++;
|
|
|
|
if (IN_TRANSFER(TransferParameters)) {
|
|
td->HwTD.Asy.Direction = HcTDDirection_In;
|
|
} else {
|
|
td->HwTD.Asy.Direction = HcTDDirection_Out;
|
|
}
|
|
td->HwTD.Asy.IntDelay = HcTDIntDelay_NoInterrupt;
|
|
td->HwTD.Asy.Toggle = toggleForDataPhase;
|
|
td->HwTD.Asy.ConditionCode = HcCC_NotAccessed;
|
|
|
|
// after the first TD get the toggle from ED
|
|
toggleForDataPhase = HcTDToggle_FromEd;
|
|
|
|
LOGENTRY(DeviceData,
|
|
G, '_dta', td, lengthMapped, TransferParameters->TransferBufferLength);
|
|
|
|
lengthMapped =
|
|
OHCI_MapAsyncTransferToTd(DeviceData,
|
|
EndpointData->Parameters.MaxPacketSize,
|
|
lengthMapped,
|
|
TransferContext,
|
|
td,
|
|
TransferSGList);
|
|
|
|
// allocate another TD
|
|
lastTd = td;
|
|
td = OHCI_ALLOC_TD(DeviceData, EndpointData);
|
|
OHCI_ASSERT(DeviceData, td != USB_BAD_PTR);
|
|
INITIALIZE_TD_FOR_TRANSFER(td, TransferContext);
|
|
|
|
SET_NEXT_TD(lastTd, td);
|
|
|
|
}
|
|
|
|
//
|
|
// set the shortxfer OK bit on the last TD only
|
|
//
|
|
if (SHORT_TRANSFER_OK(TransferParameters)) {
|
|
lastTd->HwTD.Asy.ShortXferOk = 1;
|
|
SET_FLAG(TransferContext->TcFlags, TC_FLAGS_SHORT_XFER_OK);
|
|
}
|
|
|
|
//
|
|
// now do the status phase
|
|
//
|
|
|
|
LOGENTRY(DeviceData, G, '_sta', td, 0, dataTDCount);
|
|
#if DBG
|
|
if (dataTDCount > 1) {
|
|
TEST_TRAP();
|
|
}
|
|
#endif
|
|
|
|
// status direction is opposite data direction,
|
|
// specify interrupt on completion
|
|
|
|
td->HwTD.Control = 0;
|
|
td->HwTD.Asy.IntDelay = HcTDIntDelay_0ms;
|
|
td->HwTD.Asy.Toggle = HcTDToggle_Data1;
|
|
td->HwTD.Asy.ConditionCode = HcCC_NotAccessed;
|
|
td->HwTD.CBP = 0;
|
|
td->HwTD.BE = 0;
|
|
|
|
// status phase moves no data
|
|
td->TransferCount = 0;
|
|
SET_FLAG(td->Flags, TD_FLAG_CONTROL_STATUS);
|
|
|
|
if (IN_TRANSFER(TransferParameters)) {
|
|
td->HwTD.Asy.Direction = HcTDDirection_Out;
|
|
} else {
|
|
td->HwTD.Asy.Direction = HcTDDirection_In;
|
|
td->HwTD.Asy.ShortXferOk = 1;
|
|
}
|
|
|
|
// count status TD
|
|
TransferContext->StatusTd = td;
|
|
TransferContext->PendingTds++;
|
|
|
|
OHCI_ASSERT(DeviceData, TransferContext->PendingTds == dataTDCount+2);
|
|
|
|
//
|
|
// now put a new dummy TD on the tail of the EP queue
|
|
//
|
|
|
|
// allocate the new dummy tail
|
|
lastTd = td;
|
|
td = OHCI_ALLOC_TD(DeviceData, EndpointData);
|
|
OHCI_ASSERT(DeviceData, td != USB_BAD_PTR);
|
|
SET_NEXT_TD(lastTd, td);
|
|
SET_NEXT_TD_NULL(td);
|
|
|
|
//
|
|
// Set new TailP in ED
|
|
// note: This is the last TD in the list and the place holder.
|
|
//
|
|
|
|
EndpointData->HcdTailP =
|
|
TransferContext->NextXferTd = td;
|
|
|
|
// put the request on the hardware queue
|
|
LOGENTRY(DeviceData, G,
|
|
'_Tal', TransferContext->PendingTds,
|
|
td->PhysicalAddress, EndpointData->HcdEd->HwED.HeadP);
|
|
|
|
EndpointData->HcdEd->HwED.TailP = td->PhysicalAddress;
|
|
|
|
// tell the hc we have control transfers available
|
|
OHCI_EnableList(DeviceData, EndpointData);
|
|
|
|
return USBMP_STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
USB_MINIPORT_STATUS
|
|
OHCI_BulkOrInterruptTransfer(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData,
|
|
PTRANSFER_PARAMETERS TransferParameters,
|
|
PTRANSFER_CONTEXT TransferContext,
|
|
PTRANSFER_SG_LIST TransferSGList
|
|
)
|
|
{
|
|
PHCD_TRANSFER_DESCRIPTOR lastTd, td;
|
|
ULONG lengthMapped;
|
|
|
|
// see if we have enough free TDs to handle this transfer
|
|
// if not return BUSY, port driver will retry later
|
|
|
|
LOGENTRY(DeviceData, G, '_ITR', EndpointData, TransferParameters,
|
|
TransferContext);
|
|
|
|
ASSERT_TRANSFER(DeviceData, TransferContext);
|
|
|
|
//if (EndpointData->PendingTransfers ==
|
|
// EndpointData->MaxPendingTransfers) {
|
|
// LOGENTRY(DeviceData, G, '_bsy', EndpointData, TransferContext,
|
|
// TransferParameters);
|
|
//
|
|
// return USBMP_STATUS_BUSY;
|
|
//}
|
|
|
|
if (TransferSGList->SgCount >
|
|
OHCI_FreeTds(DeviceData, EndpointData)) {
|
|
// not enough TDs
|
|
|
|
return USBMP_STATUS_BUSY;
|
|
}
|
|
|
|
EndpointData->PendingTransfers++;
|
|
|
|
// we have enough tds, program the transfer
|
|
|
|
LOGENTRY(DeviceData, G, '_nby', EndpointData, TransferParameters,
|
|
EndpointData->HcdEd);
|
|
|
|
//
|
|
// grab the dummy TD from the tail of the queue
|
|
//
|
|
lastTd = td = EndpointData->HcdTailP;
|
|
OHCI_ASSERT(DeviceData, td->Flags & TD_FLAG_BUSY);
|
|
|
|
//
|
|
// now setup the data TDs
|
|
//
|
|
|
|
// always build at least one data td
|
|
lengthMapped = 0;
|
|
|
|
do {
|
|
|
|
INITIALIZE_TD_FOR_TRANSFER(td, TransferContext);
|
|
|
|
//
|
|
// fields for data TD
|
|
//
|
|
|
|
td->HwTD.Control = 0;
|
|
td->HwTD.Asy.IntDelay = HcTDIntDelay_NoInterrupt;
|
|
td->HwTD.Asy.Toggle = HcTDToggle_FromEd;
|
|
td->HwTD.Asy.ConditionCode = HcCC_NotAccessed;
|
|
|
|
if (IN_TRANSFER(TransferParameters)) {
|
|
td->HwTD.Asy.Direction = HcTDDirection_In;
|
|
} else {
|
|
// short transfers are OK on out packets.
|
|
// actually I'm not even sure what this does
|
|
// for outbound requests
|
|
td->HwTD.Asy.Direction = HcTDDirection_Out;
|
|
td->HwTD.Asy.ShortXferOk = 1;
|
|
}
|
|
|
|
LOGENTRY(DeviceData,
|
|
G, '_ita', td, lengthMapped, TransferParameters->TransferBufferLength);
|
|
TransferContext->PendingTds++;
|
|
|
|
if (TransferParameters->TransferBufferLength != 0) {
|
|
lengthMapped =
|
|
OHCI_MapAsyncTransferToTd(DeviceData,
|
|
EndpointData->Parameters.MaxPacketSize,
|
|
lengthMapped,
|
|
TransferContext,
|
|
td,
|
|
TransferSGList);
|
|
} else {
|
|
OHCI_ASSERT(DeviceData, TransferSGList->SgCount == 0);
|
|
|
|
td->HwTD.CBP = 0;
|
|
td->HwTD.BE = 0;
|
|
td->TransferCount = 0;
|
|
}
|
|
|
|
// allocate another TD
|
|
lastTd = td;
|
|
td = OHCI_ALLOC_TD(DeviceData, EndpointData);
|
|
OHCI_ASSERT(DeviceData, td != USB_BAD_PTR);
|
|
SET_NEXT_TD(lastTd, td);
|
|
|
|
} while (lengthMapped < TransferParameters->TransferBufferLength);
|
|
|
|
//
|
|
// About ShortXferOk:
|
|
//
|
|
// This bit will trigger the controller to generate an error
|
|
// and halt the ed if it is not set. The client may specify
|
|
// behavior on short transfers (packets) in the transfersFlags
|
|
// field of the URB.
|
|
//
|
|
|
|
// we must not set short transfer OK on split transfers since
|
|
// the next transfer may not be a new transfer
|
|
|
|
if (SHORT_TRANSFER_OK(TransferParameters) &&
|
|
!TEST_FLAG(TransferParameters->MiniportFlags, MPTX_SPLIT_TRANSFER)) {
|
|
|
|
// we can only set this bit in the last TD of the
|
|
// transfer since that TD points to the next transfer.
|
|
//
|
|
// All other TDs must still generate an error and the
|
|
// ed must be resumed by us.
|
|
|
|
lastTd->HwTD.Asy.ShortXferOk = 1;
|
|
SET_FLAG(TransferContext->TcFlags, TC_FLAGS_SHORT_XFER_OK);
|
|
}
|
|
|
|
lastTd->HwTD.Asy.IntDelay = HcTDIntDelay_0ms;
|
|
|
|
//
|
|
// now put a new dummy TD on the tail of the EP queue
|
|
//
|
|
|
|
SET_NEXT_TD(lastTd, td);
|
|
SET_NEXT_TD_NULL(td);
|
|
|
|
|
|
//
|
|
// Set new TailP in ED
|
|
// note: This is the last TD in the list and the place holder.
|
|
//
|
|
|
|
TransferContext->NextXferTd =
|
|
EndpointData->HcdTailP = td;
|
|
|
|
// put the request on the hardware queue
|
|
LOGENTRY(DeviceData, G,
|
|
'_Tal', TransferContext->PendingTds ,
|
|
td->PhysicalAddress, EndpointData->HcdEd->HwED.HeadP);
|
|
|
|
EndpointData->HcdEd->HwED.TailP = td->PhysicalAddress;
|
|
|
|
LOGENTRY(DeviceData, G, '_ego', EndpointData->HcdHeadP,
|
|
TransferContext->TcFlags, 0);
|
|
|
|
// tell the hc we have bulk/interrupt transfers available
|
|
OHCI_EnableList(DeviceData, EndpointData);
|
|
|
|
return USBMP_STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
USB_MINIPORT_STATUS
|
|
OHCI_OpenControlEndpoint(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_PARAMETERS EndpointParameters,
|
|
PENDPOINT_DATA EndpointData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PUCHAR buffer;
|
|
HW_32BIT_PHYSICAL_ADDRESS phys, edPhys;
|
|
PHCD_ENDPOINT_DESCRIPTOR ed;
|
|
ULONG i, available, tdCount;
|
|
|
|
LOGENTRY(DeviceData, G, '_opC', 0, 0, 0);
|
|
|
|
buffer = EndpointParameters->CommonBufferVa;
|
|
phys = EndpointParameters->CommonBufferPhys;
|
|
available = EndpointParameters->CommonBufferBytes;
|
|
|
|
#if DBG
|
|
{
|
|
ULONG offset;
|
|
|
|
offset = BYTE_OFFSET(buffer);
|
|
|
|
// OHCI requires 16 byte alignemnt
|
|
OHCI_ASSERT(DeviceData, (offset % 16) == 0);
|
|
}
|
|
#endif
|
|
|
|
// use control list
|
|
EndpointData->StaticEd =
|
|
&DeviceData->StaticEDList[ED_CONTROL];
|
|
|
|
// make the Ed
|
|
ed = (PHCD_ENDPOINT_DESCRIPTOR) buffer;
|
|
|
|
edPhys = phys;
|
|
phys += sizeof(HCD_ENDPOINT_DESCRIPTOR);
|
|
buffer += sizeof(HCD_ENDPOINT_DESCRIPTOR);
|
|
available -= sizeof(HCD_ENDPOINT_DESCRIPTOR);
|
|
|
|
EndpointData->TdList = (PHCD_TD_LIST) buffer;
|
|
|
|
tdCount = available/sizeof(HCD_TRANSFER_DESCRIPTOR);
|
|
LOGENTRY(DeviceData, G, '_tdC', tdCount, TDS_PER_CONTROL_ENDPOINT, 0);
|
|
OHCI_ASSERT(DeviceData, tdCount >= TDS_PER_CONTROL_ENDPOINT);
|
|
|
|
EndpointData->TdCount = tdCount;
|
|
for (i=0; i<tdCount; i++) {
|
|
OHCI_InitializeTD(DeviceData,
|
|
EndpointData,
|
|
&EndpointData->TdList->Td[i],
|
|
phys);
|
|
|
|
phys += sizeof(HCD_TRANSFER_DESCRIPTOR);
|
|
}
|
|
|
|
EndpointData->HcdEd =
|
|
OHCI_InitializeED(DeviceData,
|
|
EndpointData,
|
|
ed,
|
|
&EndpointData->TdList->Td[0],
|
|
edPhys);
|
|
|
|
// control endpoints do not halt
|
|
ed->EdFlags = EDFLAG_CONTROL | EDFLAG_NOHALT;
|
|
|
|
OHCI_InsertEndpointInSchedule(DeviceData,
|
|
EndpointData);
|
|
|
|
return USBMP_STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
USB_MINIPORT_STATUS
|
|
OHCI_OpenInterruptEndpoint(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_PARAMETERS EndpointParameters,
|
|
PENDPOINT_DATA EndpointData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PUCHAR buffer;
|
|
HW_32BIT_PHYSICAL_ADDRESS phys, edPhys;
|
|
PHCD_ENDPOINT_DESCRIPTOR ed;
|
|
ULONG i, bytes, offset;
|
|
// this is an index table that converts the
|
|
// period to a list index
|
|
UCHAR periodTable[8] = {
|
|
ED_INTERRUPT_1ms, //period = 1ms
|
|
ED_INTERRUPT_2ms, //period = 2ms
|
|
ED_INTERRUPT_4ms, //period = 4ms
|
|
ED_INTERRUPT_8ms, //period = 8ms
|
|
ED_INTERRUPT_16ms,//period = 16ms
|
|
ED_INTERRUPT_32ms,//period = 32ms
|
|
ED_INTERRUPT_32ms,//period = 64ms
|
|
ED_INTERRUPT_32ms //period = 128ms
|
|
};
|
|
|
|
|
|
// carve up our common buffer
|
|
// TDS_PER_ENDPOINT TDs plus an ED
|
|
|
|
LOGENTRY(DeviceData, G, '_opI', 0, 0, EndpointParameters->Period);
|
|
|
|
|
|
// select the proper list
|
|
// the period is a power of 2 ie
|
|
// 32,16,8,4,2,1
|
|
// we just need to find which bit is set
|
|
GET_BIT_SET(EndpointParameters->Period, i);
|
|
OHCI_ASSERT(DeviceData, i < 8);
|
|
OHCI_ASSERT(DeviceData, EndpointParameters->Period < 64);
|
|
|
|
buffer = EndpointParameters->CommonBufferVa;
|
|
phys = EndpointParameters->CommonBufferPhys;
|
|
bytes = EndpointParameters->CommonBufferBytes;
|
|
offset = EndpointParameters->ScheduleOffset;
|
|
|
|
EndpointData->StaticEd =
|
|
&DeviceData->StaticEDList[periodTable[i]+offset];
|
|
|
|
LOGENTRY(DeviceData, G, '_lst', i, periodTable[i], offset);
|
|
|
|
// we found the correct base list
|
|
|
|
EndpointData->StaticEd->AllocatedBandwidth +=
|
|
EndpointParameters->Bandwidth;
|
|
|
|
// make the Ed
|
|
ed = (PHCD_ENDPOINT_DESCRIPTOR) buffer;
|
|
edPhys = phys;
|
|
phys += sizeof(HCD_ENDPOINT_DESCRIPTOR);
|
|
buffer += sizeof(HCD_ENDPOINT_DESCRIPTOR);
|
|
bytes -= sizeof(HCD_ENDPOINT_DESCRIPTOR);
|
|
|
|
EndpointData->TdList = (PHCD_TD_LIST) buffer;
|
|
EndpointData->TdCount = bytes/sizeof(HCD_TRANSFER_DESCRIPTOR);
|
|
|
|
OHCI_ASSERT(DeviceData,
|
|
EndpointData->TdCount >= TDS_PER_INTERRUPT_ENDPOINT);
|
|
// Bugbug - use what we get
|
|
for (i=0; i<EndpointData->TdCount; i++) {
|
|
OHCI_InitializeTD(DeviceData,
|
|
EndpointData,
|
|
&EndpointData->TdList->Td[i],
|
|
phys);
|
|
|
|
phys += sizeof(HCD_TRANSFER_DESCRIPTOR);
|
|
}
|
|
|
|
EndpointData->HcdEd =
|
|
OHCI_InitializeED(DeviceData,
|
|
EndpointData,
|
|
ed,
|
|
&EndpointData->TdList->Td[0],
|
|
edPhys);
|
|
|
|
OHCI_InsertEndpointInSchedule(DeviceData,
|
|
EndpointData);
|
|
|
|
return USBMP_STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
USB_MINIPORT_STATUS
|
|
OHCI_OpenBulkEndpoint(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_PARAMETERS EndpointParameters,
|
|
PENDPOINT_DATA EndpointData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PUCHAR buffer;
|
|
HW_32BIT_PHYSICAL_ADDRESS phys, edPhys;
|
|
PHCD_ENDPOINT_DESCRIPTOR ed;
|
|
ULONG i, bytes;
|
|
|
|
LOGENTRY(DeviceData, G, '_opB', 0, 0, 0);
|
|
|
|
buffer = EndpointParameters->CommonBufferVa;
|
|
phys = EndpointParameters->CommonBufferPhys;
|
|
bytes = EndpointParameters->CommonBufferBytes;
|
|
|
|
// use control list
|
|
EndpointData->StaticEd =
|
|
&DeviceData->StaticEDList[ED_BULK];
|
|
|
|
// make the Ed
|
|
ed = (PHCD_ENDPOINT_DESCRIPTOR) buffer;
|
|
|
|
edPhys = phys;
|
|
phys += sizeof(HCD_ENDPOINT_DESCRIPTOR);
|
|
buffer += sizeof(HCD_ENDPOINT_DESCRIPTOR);
|
|
bytes -= sizeof(HCD_ENDPOINT_DESCRIPTOR);
|
|
|
|
EndpointData->TdList = (PHCD_TD_LIST) buffer;
|
|
EndpointData->TdCount = bytes/sizeof(HCD_TRANSFER_DESCRIPTOR);
|
|
|
|
OHCI_ASSERT(DeviceData,
|
|
EndpointData->TdCount >= TDS_PER_BULK_ENDPOINT);
|
|
// Bugbug - use what we get
|
|
for (i=0; i<EndpointData->TdCount; i++) {
|
|
OHCI_InitializeTD(DeviceData,
|
|
EndpointData,
|
|
&EndpointData->TdList->Td[i],
|
|
phys);
|
|
|
|
phys += sizeof(HCD_TRANSFER_DESCRIPTOR);
|
|
}
|
|
|
|
EndpointData->HcdEd =
|
|
OHCI_InitializeED(DeviceData,
|
|
EndpointData,
|
|
ed,
|
|
&EndpointData->TdList->Td[0],
|
|
edPhys);
|
|
|
|
OHCI_InsertEndpointInSchedule(DeviceData,
|
|
EndpointData);
|
|
|
|
return USBMP_STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
//
|
|
// When the HEADP is set to a new value we risk loosing
|
|
// the current data toggle stored there.
|
|
// This macro resets headp and preserves the flags which
|
|
// include the toggle.
|
|
//
|
|
#define RESET_HEADP(dd, ed, address) \
|
|
{\
|
|
ULONG headp;\
|
|
headp = ((ed)->HwED.HeadP & HcEDHeadP_FLAGS) | (address);\
|
|
LOGENTRY((dd), G, '_rhp', headp, (ed), 0); \
|
|
(ed)->HwED.HeadP = headp; \
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
OHCI_PollAsyncEndpoint(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData
|
|
)
|
|
/*++
|
|
|
|
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_ENDPOINT_DESCRIPTOR ed;
|
|
ULONG i;
|
|
PTRANSFER_CONTEXT transfer;
|
|
BOOLEAN clearHalt = FALSE;
|
|
HW_32BIT_PHYSICAL_ADDRESS headP;
|
|
|
|
ed = EndpointData->HcdEd;
|
|
|
|
LOGENTRY(DeviceData, G, '_pol', ed, EndpointData, 0);
|
|
|
|
// note it is important the the compiler generate a
|
|
// dword move when reading the queuehead HeadP register
|
|
// since this location is also accessed by the host
|
|
// hardware
|
|
headP = ed->HwED.HeadP;
|
|
|
|
// get the 'currentTD'
|
|
currentTd = (PHCD_TRANSFER_DESCRIPTOR)
|
|
USBPORT_PHYSICAL_TO_VIRTUAL(headP & ~HcEDHeadP_FLAGS,
|
|
DeviceData,
|
|
EndpointData);
|
|
|
|
LOGENTRY(DeviceData, G, '_cTD', currentTd,
|
|
headP & ~HcEDHeadP_FLAGS,
|
|
TRANSFER_CONTEXT_PTR(currentTd->TransferContext));
|
|
|
|
|
|
if (ed->HwED.HeadP & HcEDHeadP_HALT) {
|
|
// ed is 'halted'
|
|
LOGENTRY(DeviceData, G, '_hlt', ed, EndpointData->HcdHeadP, 0);
|
|
|
|
clearHalt = (BOOLEAN) (ed->EdFlags & EDFLAG_NOHALT);
|
|
|
|
// walk the swHeadP to the currentTD this (this will
|
|
// be the first TD after the offending TD)
|
|
|
|
td = EndpointData->HcdHeadP;
|
|
while (td != currentTd) {
|
|
|
|
transfer = TRANSFER_CONTEXT_PTR(td->TransferContext);
|
|
ASSERT_TRANSFER(DeviceData, transfer);
|
|
|
|
OHCI_ASSERT(DeviceData, !TEST_FLAG(td->Flags, TD_FLAG_DONE));
|
|
LOGENTRY(DeviceData, G, '_wtd', td, transfer->TcFlags, transfer);
|
|
|
|
if (td->HwTD.Asy.ConditionCode == HcCC_NoError) {
|
|
// not the offending TD,
|
|
// mark this TD done
|
|
SET_FLAG(td->Flags, TD_FLAG_DONE);
|
|
OHCI_ASSERT(DeviceData, td->DoneLink.Flink == NULL &&
|
|
td->DoneLink.Blink == NULL);
|
|
InsertTailList(&EndpointData->DoneTdList,
|
|
&td->DoneLink);
|
|
} else {
|
|
// some kind of error
|
|
if (td->HwTD.Asy.ConditionCode == HcCC_NotAccessed) {
|
|
|
|
// if the 'current transfer' is DONE because
|
|
// of a short packet then the remaining TDs
|
|
// need to be flushed out.
|
|
// current TD should be pointing at the next
|
|
// TD to run (next transfer or status for control)
|
|
|
|
SET_FLAG(td->Flags, TD_FLAG_DONE);
|
|
SET_FLAG(td->Flags, TD_FLAG_SKIP);
|
|
OHCI_ASSERT(DeviceData, td->DoneLink.Flink == NULL &&
|
|
td->DoneLink.Blink == NULL);
|
|
InsertTailList(&EndpointData->DoneTdList,
|
|
&td->DoneLink);
|
|
|
|
LOGENTRY(DeviceData, G, '_fld', td, 0, 0);
|
|
|
|
} else if (td->HwTD.Asy.ConditionCode == HcCC_DataUnderrun &&
|
|
TEST_FLAG(transfer->TcFlags, TC_FLAGS_SHORT_XFER_OK)) {
|
|
|
|
// special case HcCC_DataUnderrun. this error
|
|
// needs to be ignored if shortxferOK is set.
|
|
|
|
// cases handled (HcCC_DataUnderrun):
|
|
//
|
|
// 1. control transfer and error before the status phase w/
|
|
// short xfer OK
|
|
// we need to advance to the status phase and ignore
|
|
// error and resume ep
|
|
//
|
|
// 2. interrupt/bulk with short xfer OK, ignore the error
|
|
// advance to the next transfer resume ep
|
|
//
|
|
|
|
LOGENTRY(DeviceData, G, '_sok', td, 0, 0);
|
|
|
|
|
|
// reset the error on the offending Td
|
|
td->HwTD.Asy.ConditionCode = HcCC_NoError;
|
|
// resume the ep
|
|
clearHalt = TRUE;
|
|
|
|
// if this is a control transfer bump
|
|
// HW headp to the status phase
|
|
if (!TEST_FLAG(td->Flags, TD_FLAG_CONTROL_STATUS) &&
|
|
transfer->StatusTd != NULL) {
|
|
// control transfer data phase, bump
|
|
// HW headp to the status phase
|
|
TEST_TRAP();
|
|
RESET_HEADP(DeviceData, ed, transfer->StatusTd->PhysicalAddress);
|
|
currentTd = transfer->StatusTd;
|
|
} else {
|
|
|
|
// if the current transfer is a split we must flush
|
|
// all other split elements as well.
|
|
|
|
if (transfer->TransferParameters->MiniportFlags &
|
|
MPTX_SPLIT_TRANSFER) {
|
|
|
|
PTRANSFER_CONTEXT tmpTransfer;
|
|
PHCD_TRANSFER_DESCRIPTOR tmpTd;
|
|
ULONG seq;
|
|
|
|
TEST_TRAP();
|
|
|
|
seq = transfer->TransferParameters->SequenceNumber;
|
|
tmpTd = transfer->NextXferTd;
|
|
tmpTransfer =
|
|
TRANSFER_CONTEXT_PTR(tmpTd->TransferContext);
|
|
|
|
// find the first tranfer with a new sequence
|
|
// number or the tail of the list
|
|
|
|
while (tmpTransfer != FREE_TD_CONTEXT &&
|
|
tmpTransfer->TransferParameters->SequenceNumber
|
|
== seq) {
|
|
|
|
// mark all TDs done for this transfer
|
|
|
|
tmpTd = tmpTransfer->NextXferTd;
|
|
tmpTransfer =
|
|
TRANSFER_CONTEXT_PTR(tmpTd->TransferContext);
|
|
}
|
|
|
|
RESET_HEADP(DeviceData, ed, tmpTd->PhysicalAddress);
|
|
currentTd = tmpTd;
|
|
|
|
} else {
|
|
// bump HW headp to the next transfer
|
|
RESET_HEADP(DeviceData, ed, transfer->NextXferTd->PhysicalAddress);
|
|
currentTd = transfer->NextXferTd;
|
|
|
|
}
|
|
}
|
|
|
|
SET_FLAG(td->Flags, TD_FLAG_DONE);
|
|
OHCI_ASSERT(DeviceData, td->DoneLink.Flink == NULL &&
|
|
td->DoneLink.Blink == NULL);
|
|
InsertTailList(&EndpointData->DoneTdList,
|
|
&td->DoneLink);
|
|
|
|
} else {
|
|
// general error, mark the TD as completed
|
|
// update Headp to point to the next transfer
|
|
LOGENTRY(DeviceData, G, '_ger', td, 0, 0);
|
|
|
|
SET_FLAG(td->Flags, TD_FLAG_DONE);
|
|
OHCI_ASSERT(DeviceData, td->DoneLink.Flink == NULL &&
|
|
td->DoneLink.Blink == NULL);
|
|
InsertTailList(&EndpointData->DoneTdList,
|
|
&td->DoneLink);
|
|
RESET_HEADP(DeviceData, ed, transfer->NextXferTd->PhysicalAddress)
|
|
currentTd = transfer->NextXferTd;
|
|
|
|
}
|
|
}
|
|
// we walk the SW links
|
|
td = TRANSFER_DESCRIPTOR_PTR(td->NextHcdTD);
|
|
|
|
} /* while */
|
|
|
|
} else {
|
|
|
|
// ed is not 'halted'
|
|
|
|
// First walk the swHeadP to the current TD (hw headp)
|
|
// mark all TDs we find as completed
|
|
//
|
|
// NOTE: this step may be skipped if the
|
|
// done queue is reliable
|
|
|
|
td = EndpointData->HcdHeadP;
|
|
|
|
LOGENTRY(DeviceData, G, '_nht', td, currentTd, 0);
|
|
|
|
while (td != currentTd) {
|
|
LOGENTRY(DeviceData, G, '_mDN', td, 0, 0);
|
|
SET_FLAG(td->Flags, TD_FLAG_DONE);
|
|
OHCI_ASSERT(DeviceData, td->DoneLink.Flink == NULL &&
|
|
td->DoneLink.Blink == NULL);
|
|
InsertTailList(&EndpointData->DoneTdList,
|
|
&td->DoneLink);
|
|
|
|
td = TRANSFER_DESCRIPTOR_PTR(td->NextHcdTD);
|
|
}
|
|
}
|
|
|
|
// set the sw headp to the new current head
|
|
EndpointData->HcdHeadP = currentTd;
|
|
|
|
// now flush all completed TDs
|
|
// do this in order of completion
|
|
|
|
// now flush all completed TDs. Do it in order of completion.
|
|
while (!IsListEmpty(&EndpointData->DoneTdList)) {
|
|
|
|
PLIST_ENTRY listEntry;
|
|
|
|
listEntry = RemoveHeadList(&EndpointData->DoneTdList);
|
|
|
|
|
|
td = (PHCD_TRANSFER_DESCRIPTOR) CONTAINING_RECORD(
|
|
listEntry,
|
|
struct _HCD_TRANSFER_DESCRIPTOR,
|
|
DoneLink);
|
|
|
|
|
|
if ((td->Flags & (TD_FLAG_XFER | TD_FLAG_DONE)) ==
|
|
(TD_FLAG_XFER | TD_FLAG_DONE)) {
|
|
|
|
OHCI_ProcessDoneAsyncTd(DeviceData,
|
|
td,
|
|
TRUE);
|
|
}
|
|
|
|
}
|
|
#if 0
|
|
for (i=0; i<EndpointData->TdCount; i++) {
|
|
td = &EndpointData->TdList->Td[i];
|
|
|
|
if ((td->Flags & (TD_FLAG_XFER | TD_FLAG_DONE)) ==
|
|
(TD_FLAG_XFER | TD_FLAG_DONE)) {
|
|
OHCI_ProcessDoneAsyncTd(DeviceData,
|
|
td,
|
|
TRUE);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (clearHalt) {
|
|
// auto clear the halt condition and
|
|
// resume processing on the endpoint
|
|
LOGENTRY(DeviceData, G, '_cht', ed, 0, 0);
|
|
ed->HwED.HeadP &= ~HcEDHeadP_HALT;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
OHCI_ProcessDoneAsyncTd(
|
|
PDEVICE_DATA DeviceData,
|
|
PHCD_TRANSFER_DESCRIPTOR Td,
|
|
BOOLEAN CompleteTransfer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
process a completed TD
|
|
|
|
Parameters
|
|
|
|
--*/
|
|
{
|
|
PTRANSFER_CONTEXT transferContext;
|
|
PENDPOINT_DATA endpointData;
|
|
USBD_STATUS usbdStatus;
|
|
|
|
transferContext = TRANSFER_CONTEXT_PTR(Td->TransferContext);
|
|
|
|
transferContext->PendingTds--;
|
|
endpointData = transferContext->EndpointData;
|
|
|
|
LOGENTRY(DeviceData, G, '_Dtd', transferContext,
|
|
Td->HwTD.Asy.ConditionCode,
|
|
Td);
|
|
|
|
if (TEST_FLAG(Td->Flags, TD_FLAG_SKIP)) {
|
|
|
|
OHCI_ASSERT(DeviceData, HcCC_NotAccessed == Td->HwTD.Asy.ConditionCode);
|
|
// td was unused, part of short-transfer
|
|
LOGENTRY(DeviceData, G, '_skT', Td, transferContext, 0);
|
|
Td->HwTD.Asy.ConditionCode = HcCC_NoError;
|
|
|
|
} else {
|
|
|
|
if (Td->HwTD.CBP) {
|
|
//
|
|
// A value of 0 here indicates a zero length data packet
|
|
// or that all bytes have been transfered.
|
|
//
|
|
// A non-zero value means we recieved a short packet and
|
|
// therefore need to adjust the transferCount to reflect bytes
|
|
// transferred
|
|
|
|
//
|
|
// The buffer is only spec'ed for length up to two 4K pages.
|
|
// (BE is the physical address of the last byte in the
|
|
// TD buffer. CBP is the current byte pointer)
|
|
//
|
|
// TransferCount is intailized to the number of bytes to transfer,
|
|
// we need to subtract the difference between the end and
|
|
// current ptr (ie end-current = bytes not transferred) and
|
|
// update the TransferCount.
|
|
|
|
// transfer count should never go negative
|
|
// TransferCount will be zero on the status
|
|
// phase of a control transfer so we skip
|
|
// the calculation
|
|
|
|
if (Td->TransferCount) {
|
|
Td->TransferCount -=
|
|
/* have we gone further than a page? */
|
|
((((Td->HwTD.BE ^ Td->HwTD.CBP) & ~OHCI_PAGE_SIZE_MASK)
|
|
? OHCI_PAGE_SIZE : 0) +
|
|
/* minus the data buffer not used */
|
|
((Td->HwTD.BE & OHCI_PAGE_SIZE_MASK) -
|
|
(Td->HwTD.CBP & OHCI_PAGE_SIZE_MASK)+1));
|
|
}
|
|
LOGENTRY(DeviceData, G, '_xfB', Td->HwTD.BE & OHCI_PAGE_SIZE_MASK,
|
|
Td->HwTD.CBP & OHCI_PAGE_SIZE_MASK,
|
|
Td->TransferCount);
|
|
}
|
|
|
|
if (HcTDDirection_Setup != Td->HwTD.Asy.Direction) {
|
|
|
|
// data phase of a control transfer or a bulk/int
|
|
// data transfer
|
|
LOGENTRY(DeviceData, G, '_Idt', Td, transferContext, Td->TransferCount);
|
|
|
|
transferContext->BytesTransferred += Td->TransferCount;
|
|
}
|
|
|
|
if (HcCC_NoError == Td->HwTD.Asy.ConditionCode) {
|
|
|
|
LOGENTRY(DeviceData, G, '_tOK', Td->HwTD.CBP, 0, 0);
|
|
|
|
} else {
|
|
// map the error to code in USBDI.H
|
|
|
|
transferContext->UsbdStatus =
|
|
(Td->HwTD.Asy.ConditionCode | 0xC0000000);
|
|
|
|
LOGENTRY(DeviceData, G, '_tER', transferContext->UsbdStatus, 0, 0);
|
|
}
|
|
}
|
|
|
|
// mark the TD free
|
|
OHCI_FREE_TD(DeviceData, endpointData, Td);
|
|
|
|
if (transferContext->PendingTds == 0 && CompleteTransfer) {
|
|
// all TDs for this transfer are done
|
|
// clear the HAVE_TRANSFER flag to indicate
|
|
// we can teake another
|
|
endpointData->PendingTransfers--;
|
|
|
|
LOGENTRY(DeviceData, G, '_cpt',
|
|
transferContext->UsbdStatus,
|
|
transferContext,
|
|
transferContext->BytesTransferred);
|
|
|
|
USBPORT_COMPLETE_TRANSFER(DeviceData,
|
|
endpointData,
|
|
transferContext->TransferParameters,
|
|
transferContext->UsbdStatus,
|
|
transferContext->BytesTransferred);
|
|
}
|
|
}
|
|
|
|
// figure out which sgentry a particular offset in to
|
|
// a client buffer falls
|
|
#define GET_SG_INDEX(sg, i, offset)\
|
|
do {\
|
|
for((i)=0; (i) < (sg)->SgCount; (i)++) {\
|
|
if ((offset) >= (sg)->SgEntry[(i)].StartOffset &&\
|
|
(offset) < (sg)->SgEntry[(i)].StartOffset+\
|
|
(sg)->SgEntry[(i)].Length) {\
|
|
break;\
|
|
}\
|
|
}\
|
|
} while (0)
|
|
|
|
#define GET_SG_OFFSET(sg, i, offset, sgoffset)\
|
|
(sgoffset) = (offset) - (sg)->SgEntry[(i)].StartOffset
|
|
|
|
|
|
ULONG
|
|
OHCI_MapAsyncTransferToTd(
|
|
PDEVICE_DATA DeviceData,
|
|
ULONG MaxPacketSize,
|
|
ULONG LengthMapped,
|
|
PTRANSFER_CONTEXT TransferContext,
|
|
PHCD_TRANSFER_DESCRIPTOR Td,
|
|
PTRANSFER_SG_LIST SgList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Maps a data buffer to TDs according to OHCI rules
|
|
|
|
An OHCI TD can cover up to 8k with a single page crossing.
|
|
|
|
Each sg entry represents one 4k OHCI 'page'
|
|
|
|
x = pagebreak
|
|
c = current ptr
|
|
b = buffer start
|
|
e = buffer end
|
|
|
|
|
|
{..sg[sgIdx]..}
|
|
b...|---
|
|
x--c----
|
|
[ ]
|
|
\
|
|
sgOffset
|
|
[ ]
|
|
\
|
|
LengthMapped
|
|
|
|
|
|
case 1: (1 sg entry remains)
|
|
(A)- transfer < 4k, no page breaks (if c=b sgOffset = 0)
|
|
|
|
{.sg0...}
|
|
| b---->e
|
|
x-c------x
|
|
[..TD.]
|
|
|
|
(B)- last part of a transfer
|
|
|
|
{..sgN..}
|
|
b.....|.c---->e
|
|
x--------x
|
|
[..TD.]
|
|
|
|
case 2: (2 sg entries remain)
|
|
(A)- transfer < 8k, one page break (if c=b sgOffset = 0)
|
|
|
|
{..sg0..}{..sg1..}
|
|
| b----|----->e
|
|
x---c----x--------x
|
|
[.....TD....]
|
|
|
|
(B)- last 8k of transfer
|
|
|
|
{.sgN-1.}{..sgN..}
|
|
b....|--------|---->e
|
|
x-c------x--------x
|
|
[.....TD.......]
|
|
|
|
case 3: (3+ sg entries remain)
|
|
(A)- transfer 8k, two page breaks (c=b)
|
|
|
|
{..sg0..}{..sg1..}{..sg2..}
|
|
b----|--------|--->e
|
|
x---c----x--------x--------x
|
|
[.....TD...<>]
|
|
<>=<TD length must be multiple of MaxPacketSize>
|
|
|
|
(B)- continuation of large tarnsfer
|
|
|
|
{.sgN-2.}{.sgN-1.}{..sgN..}
|
|
b..|--------------------->e
|
|
x--c-----x--------x--------x
|
|
[.....TD......]
|
|
<TD length must be multiple of MaxPacketSize>
|
|
|
|
Interesting DMA tests (USBTEST):
|
|
|
|
length, offset - cases hit
|
|
|
|
4096 0 - 1a
|
|
4160 0 - 2a
|
|
4096 512 - 2a
|
|
8192 512 - 3a, 1b
|
|
8192 513 - 3a, 2b
|
|
12288 1 - 3a, 3b, 2b
|
|
|
|
Arguments:
|
|
|
|
Returns:
|
|
|
|
LengthMapped
|
|
|
|
--*/
|
|
{
|
|
HW_32BIT_PHYSICAL_ADDRESS logicalStart, logicalEnd;
|
|
ULONG sgIdx, sgOffset;
|
|
ULONG lengthThisTd;
|
|
PTRANSFER_PARAMETERS transferParameters;
|
|
|
|
// A TD can have up to one page crossing. This means we
|
|
// can put two sg entries in to one TD, one for the first
|
|
// physical page, and one for the second.
|
|
|
|
// point to first entry
|
|
|
|
LOGENTRY(DeviceData, G, '_Mpr', TransferContext,
|
|
0, LengthMapped);
|
|
|
|
transferParameters = TransferContext->TransferParameters;
|
|
|
|
OHCI_ASSERT(DeviceData, SgList->SgCount != 0);
|
|
|
|
GET_SG_INDEX(SgList, sgIdx, LengthMapped);
|
|
LOGENTRY(DeviceData, G, '_Mpp', SgList, 0, sgIdx);
|
|
OHCI_ASSERT(DeviceData, sgIdx < SgList->SgCount);
|
|
|
|
// check for one special case where the SG entries
|
|
// all map to the same physical page
|
|
if (TEST_FLAG(SgList->SgFlags, USBMP_SGFLAG_SINGLE_PHYSICAL_PAGE)) {
|
|
// in this case we map each sg entry to a single TD
|
|
LOGENTRY(DeviceData, G, '_cOD', SgList, 0, sgIdx);
|
|
|
|
// TEST_TRAP();
|
|
|
|
// adjust for the amount of buffer consumed by the
|
|
// previous TD
|
|
logicalStart =
|
|
SgList->SgEntry[sgIdx].LogicalAddress.Hw32;
|
|
|
|
lengthThisTd = SgList->SgEntry[sgIdx].Length;
|
|
|
|
logicalEnd = SgList->SgEntry[sgIdx].LogicalAddress.Hw32;
|
|
logicalEnd += lengthThisTd;
|
|
|
|
OHCI_ASSERT(DeviceData, lengthThisTd <= OHCI_PAGE_SIZE)
|
|
|
|
goto OHCI_MapAsyncTransferToTd_Done;
|
|
|
|
}
|
|
|
|
if ((SgList->SgCount-sgIdx) == 1) {
|
|
// first case, 1 entries left
|
|
// ie <4k, 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);
|
|
|
|
}
|
|
#endif
|
|
lengthThisTd =
|
|
transferParameters->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
|
|
logicalStart =
|
|
SgList->SgEntry[sgIdx].LogicalAddress.Hw32 + sgOffset;
|
|
lengthThisTd -= sgOffset;
|
|
|
|
logicalEnd = SgList->SgEntry[sgIdx].LogicalAddress.Hw32;
|
|
logicalEnd += lengthThisTd;
|
|
|
|
LOGENTRY(DeviceData, G, '_sg1', logicalStart, 0, logicalEnd);
|
|
|
|
} else if ((SgList->SgCount - sgIdx) == 2) {
|
|
|
|
// second case, 2 entries left
|
|
// ie <8k we can also fit this in
|
|
// a single TD.
|
|
#if DBG
|
|
if (sgIdx == 0) {
|
|
// case 2A
|
|
// USBT dma test length 4160 offset 0
|
|
// will hit this case
|
|
LOGENTRY(DeviceData, G, '_c2a', SgList, 0, sgIdx);
|
|
|
|
} else {
|
|
// case 2B
|
|
// USBT dma test length 8192 offset 513
|
|
// will hit this case
|
|
LOGENTRY(DeviceData, G, '_c2b', SgList, 0, sgIdx);
|
|
//TEST_TRAP();
|
|
// bugbug run with DMA test
|
|
}
|
|
#endif
|
|
lengthThisTd =
|
|
transferParameters->TransferBufferLength - LengthMapped;
|
|
|
|
// compute offset into first TD
|
|
GET_SG_OFFSET(SgList, sgIdx, LengthMapped, sgOffset);
|
|
LOGENTRY(DeviceData, G, '_sgO', sgOffset, sgIdx, LengthMapped);
|
|
#if DBG
|
|
if (sgIdx == 0) {
|
|
OHCI_ASSERT(DeviceData, sgOffset == 0);
|
|
}
|
|
#endif
|
|
|
|
// adjust pointers for amount consumed by previous TD
|
|
logicalStart = SgList->SgEntry[sgIdx].LogicalAddress.Hw32 +
|
|
sgOffset;
|
|
|
|
logicalEnd = SgList->SgEntry[sgIdx+1].LogicalAddress.Hw32;
|
|
logicalEnd += SgList->SgEntry[sgIdx+1].Length;
|
|
|
|
LOGENTRY(DeviceData, G, '_sg2', logicalStart,
|
|
lengthThisTd, logicalEnd);
|
|
|
|
} else {
|
|
// third case, more than 2 sg entries.
|
|
//
|
|
ULONG adjust, packetCount;
|
|
#if DBG
|
|
if (sgIdx == 0) {
|
|
// case 3A
|
|
// USBT dma test length 8192 offset 512
|
|
// will hit this case
|
|
LOGENTRY(DeviceData, G, '_c3a', SgList, 0, sgIdx);
|
|
|
|
} else {
|
|
// case 3B
|
|
// USBT dma test length 12288 offset 1
|
|
// will hit this case
|
|
LOGENTRY(DeviceData, G, '_c3b', SgList, 0, sgIdx);
|
|
|
|
}
|
|
#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) {
|
|
OHCI_ASSERT(DeviceData, sgOffset == 0);
|
|
}
|
|
#endif
|
|
//
|
|
// consume the next two sg entrys
|
|
//
|
|
logicalStart = SgList->SgEntry[sgIdx].LogicalAddress.Hw32+
|
|
sgOffset;
|
|
|
|
logicalEnd = SgList->SgEntry[sgIdx+1].LogicalAddress.Hw32+
|
|
SgList->SgEntry[sgIdx+1].Length;
|
|
|
|
lengthThisTd = SgList->SgEntry[sgIdx].Length +
|
|
SgList->SgEntry[sgIdx+1].Length -
|
|
sgOffset;
|
|
|
|
// round TD length down to the highest multiple
|
|
// of max_packet size
|
|
|
|
packetCount = lengthThisTd/MaxPacketSize;
|
|
LOGENTRY(DeviceData, G, '_sg3', logicalStart, packetCount, logicalEnd);
|
|
|
|
adjust = lengthThisTd - packetCount*MaxPacketSize;
|
|
|
|
lengthThisTd = packetCount*MaxPacketSize;
|
|
if (adjust) {
|
|
OHCI_ASSERT(DeviceData, adjust > (logicalEnd & 0x00000FFF));
|
|
logicalEnd-=adjust;
|
|
LOGENTRY(DeviceData, G, '_adj', adjust, lengthThisTd, logicalEnd);
|
|
}
|
|
|
|
OHCI_ASSERT(DeviceData, lengthThisTd != 0);
|
|
OHCI_ASSERT(DeviceData, lengthThisTd >= SgList->SgEntry[sgIdx].Length);
|
|
|
|
}
|
|
|
|
OHCI_MapAsyncTransferToTd_Done:
|
|
|
|
Td->HwTD.CBP = logicalStart;
|
|
Td->HwTD.BE = logicalEnd-1;
|
|
LengthMapped += lengthThisTd;
|
|
Td->TransferCount = lengthThisTd;
|
|
|
|
LOGENTRY(DeviceData, G, '_Mp1', LengthMapped, lengthThisTd, Td);
|
|
|
|
return LengthMapped;
|
|
}
|
|
|
|
|