📄 cpipe.cpp
字号:
m_pDummyTd.td->bfDiscard = 1;
for (int i = 1; i < m_transfer.numTDsInList; ++i) {
m_transfer.vaTDList.td[i].bfDiscard = 1;
}
// We need to complete the transfer ourselves
m_pED->paTdQueueHead = m_pED->paTdQueueTail = GetTDPhysAddr( m_pDummyTd.td );
m_pED->bfSkip = 0;
pTransfer = &m_transfer;
}
} else {
// Transfer is not the current one so just nuke it from the list of pending transfers
for (pTransfer = &m_transfer;
pTransfer != NULL && pTransfer->lpvCancelId != lpvCancelId;
pTransfer = pTransfer->lpNextTransfer)
if (pTransfer->lpNextTransfer == m_pLastTransfer)
// if anything gets removed, it'll be the last one in the list;
// otherwise, we'll need to reset this later.
m_pLastTransfer = pTransfer;
if (pTransfer == NULL && m_pLastTransfer->lpNextTransfer != NULL)
// oops - didn't find one, so reset my state
m_pLastTransfer = m_pLastTransfer->lpNextTransfer;
}
}
if ( pTransfer != NULL ) {
DEBUGCHK( pTransfer->lpvCancelId == lpvCancelId );
DEBUGCHK( AreTransferParametersValid(pTransfer) );
DWORD dwOldPermissions = SetProcPermissions(m_transfer.dwCurrentPermissions);
__try { // accessing caller's memory
*pTransfer->lpdwError = USB_CANCELED_ERROR;
*pTransfer->lpfComplete = TRUE;
*pTransfer->lpdwBytesTransferred = 0;
} __except( EXCEPTION_EXECUTE_HANDLER ) {
}
SetProcPermissions(dwOldPermissions);
// cache the callback info because *pTransfer will be transmogrified
LPTRANSFER_NOTIFY_ROUTINE lpfnCompletion = pTransfer->lpfnCallback;
LPVOID lpvParam = pTransfer->lpvCallbackParameter;
STransfer *pNext = pTransfer->lpNextTransfer;
if (pNext == m_pLastTransfer)
m_pLastTransfer = pTransfer;
FreeTransferMemory(pTransfer);
if (pNext) {
*pTransfer = *pNext; // yes, yes, it's a copy.
delete pNext;
} else {
if (m_pLastTransfer == pTransfer) {
// this is the only transfer in the queue
DEBUGCHK( pTransfer == &m_transfer );
m_fTransferInProgress = FALSE;
m_pLastTransfer = NULL;
} else {
DEBUGCHK( m_pLastTransfer->lpNextTransfer == pTransfer );
m_pLastTransfer->lpNextTransfer = NULL;
}
}
// from this point on, pTransfer is no longer what it was.
if (pTransfer == &m_transfer && m_pLastTransfer != NULL)
ScheduleTransfer();
// ok, transfer has been aborted. Perform callback if needed.
if ( lpfnCompletion ) {
__try { // calling the transfer complete callback
( *lpfnCompletion )( lpvParam );
} __except( EXCEPTION_EXECUTE_HANDLER ) {
DEBUGMSG( ZONE_ERROR, (TEXT("CQueuedPipe::AbortTransfer - exception executing completion callback function\n")) );
}
}
if ( lpCancelAddress ) {
__try { // calling the Cancel function
( *lpCancelAddress )( lpvNotifyParameter );
} __except( EXCEPTION_EXECUTE_HANDLER ) {
DEBUGMSG( ZONE_ERROR, (TEXT("CQueuedPipe::AbortTransfer - exception executing cancellation callback function\n")) );
}
}
status = requestOK;
}
LeaveCriticalSection( &m_csPipeLock );
DEBUGMSG( ZONE_TRANSFER, (TEXT("-CQueuedPipe(%s)::AbortTransfer - lpvCancelId = 0x%x, returning HCD_REQUEST_STATUS %d\n"), GetPipeType(), lpvCancelId, status) );
return status;
}
// ******************************************************************
// Scope: private
void CQueuedPipe::AbortQueue( void )
//
// Purpose: Abort the current transfer (i.e., queue of TDs).
//
// Parameters: pQH - pointer to Queue Head for transfer to abort
//
// Returns: Nothing
//
// Notes: not used for OHCI
// ******************************************************************
{
DEBUGCHK(0);
}
// ******************************************************************
// Scope: protected (Implements CPipe::CheckForDoneTransfers = 0)
BOOL CQueuedPipe::CheckForDoneTransfers( void )
//
// Purpose: Check if the transfer on this pipe is finished, and
// take the appropriate actions - i.e. remove the transfer
// data structures and call any notify routines
//
// Parameters: None
//
// Returns: TRUE if transfer is done, else FALSE
//
// Notes:
// ******************************************************************
{
DEBUGMSG( ZONE_TRANSFER, (TEXT("+CQueuedPipe(%s)::CheckForDoneTransfers\n"), GetPipeType() ) );
EnterCriticalSection( &m_csPipeLock );
DEBUGMSG( ZONE_TRANSFER && !m_fTransferInProgress, (TEXT("CQueuedPipe(%s)::CheckForDoneTransfers - queue was already finished on entry\n"), GetPipeType() ) );
DEBUGCHK( m_fTransferInProgress );
if ( m_fTransferInProgress ) {
// this is the normal case where we want to check if the queue is done
// and take appropriate action
// Shortcut exit if the transfer is still in progress
if (m_transfer.numTDsComplete < m_transfer.numTDsInList && !m_pED->bfHalted) {
LeaveCriticalSection( &m_csPipeLock );
return FALSE;
}
DEBUGCHK( AreTransferParametersValid() );
DWORD dwUsbError = USB_NO_ERROR;
DWORD dwDataTransferred = m_transfer.dwBufferSize;
if (m_pED->bfHalted) {
// Generally, the endpt is halted only on error, but we could get
// here also as the result of an allowable short packet on a TD
// other than the last one. This can only happen with transfers
// large enough to require >1 TD. If that's the case then fast-forward
// the ED and clear the halted bit. Then we can process the completion normally.
//
// WARNING: Because the ED is updated by the HC independently of the done queue,
// it is possible to wind up here before the errored TD has made it onto the
// the done queue. If that happens, just return and wait for the next frame,
// by which time the errored TD should have become fully retired.
//
// DANGER: We don't distinguish pipe types here so this code won't
// properly deal with control transfers (because of the status stage).
// That means that large control IN transfers cannot be short!
//
P_TD pTD = m_pDummyTd.td; // this is first due to mechanics of transfer insertion
{
DWORD iTD = 1;
while (pTD->bfConditionCode == USB_NO_ERROR) {
pTD = pTD->pNextTd;
DEBUGCHK( pTD != NULL && pTD->bfConditionCode != USB_NOT_ACCESSED_ERROR ); // there must be an errored TD somewhere
++iTD;
}
if (iTD > m_transfer.numTDsComplete) {
LeaveCriticalSection( &m_csPipeLock );
return FALSE;
}
}
DEBUGCHK( pTD->bfConditionCode != USB_NO_ERROR );
if (pTD->paCurBuffer != 0 && pTD->bfPID != TD_SETUP_PID)
dwDataTransferred = pTD->paCurBuffer - m_transfer.paActualBuffer;
else
dwDataTransferred = 0;
if (pTD->bfConditionCode == USB_DATA_UNDERRUN_ERROR && (m_transfer.dwFlags & USB_SHORT_TRANSFER_OK)) {
// OK, this one's just short.
DEBUGCHK( dwDataTransferred < m_transfer.dwBufferSize ); // NOT "<="
m_pED->paTdQueueHead = m_pED->paTdQueueTail; // indicates emptiness
m_pED->bfHalted = 0; // let the HC process the ED again
} else {
// This is really an error.
dwUsbError = pTD->bfConditionCode;
DEBUGMSG( ZONE_WARNING, (TEXT("CQueuedPipe(%s)::CheckForDoneTransfers - failure on TD 0x%x, address = %d, endpoint = %d, status field = 0x%x\n"), GetPipeType(), pTD, m_bBusAddress, m_bEndpointAddress, dwUsbError ) );
if (m_pED->bfEndpointNumber != 0)
m_fIsHalted = TRUE;
}
#ifdef DEBUG
// Verify that the rest of the TDs (if any) are still pristine
for (pTD = pTD->pNextTd; pTD != NULL; pTD = pTD->pNextTd)
DEBUGCHK( pTD->bfConditionCode == USB_NOT_ACCESSED_ERROR );
#endif // DEBUG
} else if (m_transfer.dwFlags & USB_SHORT_TRANSFER_OK) {
// A short transfer can still complete without halting the EP if it was
// expected and if the last TD in the transfer is the short one.
// We could assert here that this was an IN transfer.
int iLast = m_transfer.numTDsComplete - 1;
DEBUGCHK( iLast >= 0 );
P_TD pTD = iLast ? &m_transfer.vaTDList.td[iLast] : m_pDummyTd.td;
if (pTD->paCurBuffer)
dwDataTransferred = pTD->paCurBuffer - m_transfer.paActualBuffer;
}
// Regardless of success or failure, copy the transferred data.
// IN transfer where client did not specify a buffer
// for us to DMA into. Thus, we created our own internal
// buffer, and now need to copy the data.
if ( (m_transfer.dwFlags & USB_IN_TRANSFER) &&
m_transfer.paClientBuffer == 0 ) {
DEBUGCHK( m_transfer.lpvClientBuffer != NULL &&
m_transfer.paActualBuffer != 0 &&
m_transfer.vaActualBuffer != NULL );
DWORD dwOldPermissions = SetProcPermissions(m_transfer.dwCurrentPermissions);
__try {
memcpy( PUCHAR( m_transfer.lpvClientBuffer ), m_transfer.vaActualBuffer, dwDataTransferred );
RETAILMSG(RETAIL_ZONE_PIPE,(TEXT("CQPipe::CFDT: va: %08x pa: %08x ln: %2d to %08x: "), m_transfer.vaActualBuffer, m_transfer.paActualBuffer, dwDataTransferred, m_transfer.lpvClientBuffer ));
#if 0
{
int len = dwDataTransferred;
int i;
if( len > 16 ) len = 16;
if( len < 0 ) len = 0;
for( i = 0; i < len; i++ ){
RETAILMSG(RETAIL_ZONE_PIPE,(TEXT("%02x "), m_transfer.vaActualBuffer[i] ));
}
RETAILMSG(RETAIL_ZONE_PIPE,(TEXT("\r\n")));
}
#endif
} __except( EXCEPTION_EXECUTE_HANDLER ) {
dwDataTransferred = 0;
if (dwUsbError == USB_NO_ERROR)
dwUsbError = USB_CLIENT_BUFFER_ERROR;
}
SetProcPermissions(dwOldPermissions);
}
m_fTransferInProgress = FALSE;
DEBUGMSG(ZONE_TRANSFER, (TEXT("CQueuedPipe::CheckForDoneTransfers: completing xfr %08x at %08x\n"), m_transfer.vaTDList.td[0].pTransfer, &m_transfer));
// the TDs/Buffers associated with the transfer
// can now be freed and the ED reset
FreeTransferMemory();
m_pED->bfSkip = 1;
RETAILMSG(RETAIL_ZONE_PIPE,(TEXT("CQPipe::CFDT: Waiting one frame.\r\n")));
CHW::WaitOneFrame(); //JEFFRO: need to find a way around this
RETAILMSG(RETAIL_ZONE_PIPE,(TEXT("CQPipe::CFDT: Waiting done.\r\n")));
DWORD toggleCarry = m_pED->bfToggleCarry; // this field is actually part of QueueHead
m_pED->paTdQueueHead = m_pED->paTdQueueTail = GetTDPhysAddr( m_pDummyTd.td );
m_pED->bfToggleCarry = toggleCarry;
m_pED->bfHalted = m_fIsHalted ? 1 : 0;
m_pED->bfSkip = 0;
DWORD dwOldPermissions = SetProcPermissions(m_transfer.dwCurrentPermissions);
__try { // setting transfer status and executing callback function
*m_transfer.lpfComplete = TRUE;
*m_transfer.lpdwError = dwUsbError;
*m_transfer.lpdwBytesTransferred = dwDataTransferred;
if ( m_transfer.lpfnCallback ) {
RETAILMSG(RETAIL_ZONE_PIPE,(TEXT("CQPipe::CFDT: Calling %08x with %08x.\r\n"), m_transfer.lpfnCallback, m_transfer.lpvCallbackParameter));
( *m_transfer.lpfnCallback )( m_transfer.lpvCallbackParameter );
RETAILMSG(RETAIL_ZONE_PIPE,(TEXT("CQPipe::CFDT: Calling done.\r\n")));
}
else
{
RETAILMSG(RETAIL_ZONE_PIPE,(TEXT("CQPipe::CFDT: No completion routine on transfer to call.\r\n")));
}
} __except( EXCEPTION_EXECUTE_HANDLER ) {
DEBUGMSG( ZONE_ERROR, (TEXT("CQueuedPipe(%s)::CheckForDoneTransfers - exception setting transfer status to complete\n"), GetPipeType() ) );
}
SetProcPermissions(dwOldPermissions);
// this transfer is now officially done; are more pending?
STransfer *pNext = m_transfer.lpNextTransfer;
if (pNext) {
DEBUGCHK(m_pLastTransfer != NULL);
if (pNext == m_pLastTransfer)
m_pLastTransfer = &m_transfer;
m_transfer = *pNext; // copy for historical reasons; will fix someday.
delete pNext;
// and note that a transfer is still in progress (albeit a new one)
m_fTransferInProgress = TRUE;
ScheduleTransfer();
} else {
// this queue is now done - clean up and remove the transfer
DEBUGMSG( ZONE_TRANSFER, (TEXT("CQueuedPipe(%s)::CheckForDoneTransfers - queue was processed and is now finished\n"), GetPipeType() ) );
DEBUGCHK(m_pLastTransfer == &m_transfer);
m_fTransferInProgress = FALSE;
m_pLastTransfer = NULL;
#ifdef DEBUG
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -