windows-nt/Source/XPSP1/NT/multimedia/directx/dinput/pid/pidrd.c
2020-09-26 16:20:57 +08:00

381 lines
9.4 KiB
C

/*****************************************************************************
*
* PidRd.c
*
* Copyright (c) 1999 Microsoft Corporation. All Rights Reserved.
*
*
*
* Abstract:
*
* Read input data from PID device .
*
*****************************************************************************/
#include "pidpr.h"
#define sqfl (sqflRead)
BOOL INTERNAL
PID_IssueRead(PCPidDrv this);
BOOL INTERNAL
PID_IssueWrite(PCPidDrv this);
/*****************************************************************************
*
* @doc INTERNAL
*
* @func PCHID | pchidFromPo |
*
* Given an interior pointer to an <t OVERLAPPED>, retrieve
* a pointer to the parent <t CHid>.
*
* @parm LPOVERLAPPED | po |
*
* The pointer to convert.
*
*****************************************************************************/
PCPidDrv INLINE
pCPidDrvFromPo(LPOVERLAPPED po)
{
return (CPidDrv*) pvSubPvCb(po, FIELD_OFFSET(CPidDrv, o));
}
void CALLBACK
PID_ReadComplete(DWORD dwError, DWORD cbRead, LPOVERLAPPED po)
{
PCPidDrv this = pCPidDrvFromPo(po);
//EnterProc( PID_ReadComplete, (_"xxx", dwError, cbRead, po ));
if( !IsBadReadPtr(this, cbX(this))
&&this->cThreadRef
&& dwError == 0
//&&( this->o.InternalHigh == this->cbReport[HidP_Input] )
&&( this->hdevOvrlp != INVALID_HANDLE_VALUE ) )
{
HRESULT hres;
PUCHAR pReport;
UINT cbReport;
USHORT LinkCollection = 0x0;
USAGE rgUsages[MAX_BUTTONS] ;
USAGE UsagePage = HID_USAGE_PAGE_PID;
ULONG cAUsages = MAX_BUTTONS;
BOOL fEffectPlaying = FALSE;
LONG lEffectIndex;
pReport = this->pReport[HidP_Input];
cbReport = this->cbReport[HidP_Input];
// If the report does not belong to the PID usage page
// we should be out of here really quick.
hres = HidP_GetUsages
(HidP_Input,
UsagePage,
LinkCollection,
rgUsages,
&cAUsages,
this->ppd,
pReport,
cbReport
);
if( SUCCEEDED(hres ) )
{
UINT indx;
DWORD dwState = DIGFFS_ACTUATORSOFF | DIGFFS_USERFFSWITCHOFF | DIGFFS_POWEROFF | DIGFFS_SAFETYSWITCHOFF;
for(indx = 0x0; indx < cAUsages; indx++ )
{
USAGE Usage = rgUsages[indx];
switch(Usage)
{
case HID_USAGE_PID_EFFECT_PLAYING:
fEffectPlaying = TRUE;
break;
case HID_USAGE_PID_DEVICE_PAUSED:
dwState |= DIGFFS_PAUSED;
break;
case HID_USAGE_PID_ACTUATORS_ENABLED:
dwState |= DIGFFS_ACTUATORSON;
dwState &= ~(DIGFFS_ACTUATORSOFF);
break;
case HID_USAGE_PID_ACTUATOR_OVERRIDE_SWITCH:
dwState |= DIGFFS_USERFFSWITCHON;
dwState &= ~(DIGFFS_USERFFSWITCHOFF);
break;
case HID_USAGE_PID_SAFETY_SWITCH:
dwState |= DIGFFS_SAFETYSWITCHON;
dwState &= ~(DIGFFS_SAFETYSWITCHOFF);
break;
case HID_USAGE_PID_ACTUATOR_POWER:
dwState |= DIGFFS_POWERON;
dwState &= ~(DIGFFS_POWEROFF);
break;
default:
SquirtSqflPtszV(sqfl | sqflVerbose,
TEXT("%s: Unsupported input status usage (%x,%x:%s) "),
TEXT("PID_ReadComplete"),
UsagePage, Usage,
PIDUSAGETXT(UsagePage,Usage) );
break;
}
this->dwState = dwState;
}
hres = PID_ParseReport(
&this->ed,
&g_BlockIndexIN,
LinkCollection,
&lEffectIndex,
cbX(lEffectIndex),
pReport,
cbReport
);
if( SUCCEEDED(hres) )
{
PEFFECTSTATE pEffectState = PeffectStateFromBlockIndex(this,lEffectIndex);
if(fEffectPlaying)
{
pEffectState->lEfState |= DIEGES_PLAYING;
} else
{
pEffectState->lEfState &= ~(DIEGES_PLAYING);
}
}
}
// Issue another read
PID_IssueRead(this);
} else
{
// Boo!
}
//ExitProc();
}
BOOL INTERNAL
PID_IssueRead(PCPidDrv this)
{
BOOL fRc = FALSE;
if( !IsBadReadPtr(this, cbX(this))
&& this->cThreadRef )
{
fRc = ReadFileEx(this->hdevOvrlp, this->pReport[HidP_Input],
this->cbReport[HidP_Input], &this->o,
PID_ReadComplete);
if (fRc == FALSE)
{
SquirtSqflPtszV(sqfl | sqflError,
TEXT("FAIL ReadFileEx"));
}
}
else
{
RPF(TEXT("Bad this pointer or thread ref count!"));
}
return fRc;
}
void CALLBACK
PID_WriteComplete(DWORD dwError, DWORD cbWritten, LPOVERLAPPED po)
{
PCPidDrv this = pCPidDrvFromPo(po);
//EnterProc( PID_ReadComplete, (_"xxx", dwError, cbRead, po ));
if( !IsBadReadPtr(this, cbX(this))
&&(this->cThreadRef)
//&&( this->o.InternalHigh == this->cbWriteReport[this->blockNr] )
&&(this->hWriteComplete)
&&(this->hWrite)
&&( this->hdevOvrlp != INVALID_HANDLE_VALUE ) )
{
//if we didn't get an error & wrote everything -- or if we already tried
//twice -- we move on
if ( ((dwError == 0) && (cbWritten == this->cbWriteReport [this->blockNr]))
|| (this->dwWriteAttempt != 0)
)
{
//print a message if couldn't write a particular block
if ((dwError != 0) || (cbWritten != this->cbWriteReport [this->blockNr]))
{
SquirtSqflPtszV(sqfl | sqflError,
TEXT("Couldn't write block %u after two tries, giving up on this block."),
this->blockNr);
}
//move on
this->dwWriteAttempt = 0;
this->blockNr++;
if (this->blockNr < this->totalBlocks)
{
//write the next block
if (PID_IssueWrite(this) == FALSE)
{
//in case of failure, the callback will never be called and the completion event never set,
//so need to set it here.
SetEvent(this->hWriteComplete);
}
}
else
{
//we are done w/ this update
AssertF(this->blockNr == this->totalBlocks);
SetEvent(this->hWriteComplete);
}
}
else
{
//this is the first time we tried to write a particular block, and we failed;
//we will try once more
SquirtSqflPtszV(sqfl | sqflBenign,
TEXT("Couldn't write block %u on first attempt, retrying."),
this->blockNr);
this->dwWriteAttempt = 1;
if (PID_IssueWrite(this) == FALSE)
{
//in case of failure, the callback will never be called and the completion event never set,
//so need to set it here.
this->dwWriteAttempt = 0;
SetEvent(this->hWriteComplete);
}
}
} else
{
//need to set the completion event, otherwise we will keep waiting...
RPF(TEXT("Bad this pointer or thread ref count or handle!"));
this->dwWriteAttempt = 0;
SetEvent(this->hWriteComplete);
}
//ExitProc();
}
BOOL INTERNAL
PID_IssueWrite(PCPidDrv this)
{
BOOL fRc = FALSE;
if( !IsBadReadPtr(this, cbX(this))
&& this->cThreadRef )
{
fRc = WriteFileEx(this->hdevOvrlp, this->pWriteReport[this->blockNr],
this->cbWriteReport[this->blockNr], &this->o,
PID_WriteComplete);
if (fRc == FALSE)
{
SquirtSqflPtszV(sqfl | sqflError,
TEXT("FAIL WriteFileEx"));
}
}
else
{
RPF(TEXT("Bad this pointer or thread ref count!"));
}
return fRc;
}
VOID INTERNAL
PID_ThreadProc(CPidDrv* this)
{
MSG msg;
EnterProc( PID_ThreadProc, (_"x", this ));
AssertF(this->hdevOvrlp == INVALID_HANDLE_VALUE );
this->hdevOvrlp = CreateFile(this->tszDeviceInterface,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0, /* no SECURITY_ATTRIBUTES */
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED, /* attributes */
0); /* template */
if( this->hdevOvrlp == INVALID_HANDLE_VALUE )
{
SquirtSqflPtszV(sqfl | sqflError,
TEXT("%s:FAIL CreateFile(OverLapped)"),
s_tszProc );
}
else
//fix for mb 35282 -- no use calling PID_IssueRead() w/ an INVALID_HANDLE_VALUE for a file handle
{
if( PID_IssueRead(this) )
{
do
{
DWORD dwRc;
do
{
AssertF(this->hWrite != 0x0);
AssertF(this->hWriteComplete != 0x0);
dwRc = MsgWaitForMultipleObjectsEx(1, &this->hWrite, INFINITE, QS_ALLINPUT,
MWMO_ALERTABLE);
if (dwRc == WAIT_OBJECT_0)
{
if (PID_IssueWrite(this) == FALSE)
{
//in case of failure, the callback will never be called and the completion event never set,
//so need to set it here.
SetEvent(this->hWriteComplete);
}
}
} while ((dwRc == WAIT_IO_COMPLETION) || (dwRc == WAIT_OBJECT_0));
while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
} while(this->cThreadRef);
if( this->hdevOvrlp != INVALID_HANDLE_VALUE )
{
HANDLE hdev;
hdev = this->hdevOvrlp;
this->hdevOvrlp = INVALID_HANDLE_VALUE;
CancelIo_(hdev);
Sleep(0);
CloseHandle(hdev);
}
}
}
//close the event handles as well
if (this->hWrite != 0x0)
{
CloseHandle(this->hWrite);
this->hWrite = 0x0;
}
if (this->hWriteComplete != 0x0)
{
CloseHandle(this->hWriteComplete);
this->hWriteComplete = 0x0;
}
if(this->hThread)
{
HANDLE hdev = this->hThread;
this->hThread = NULL;
CloseHandle(hdev);
}
FreeLibraryAndExitThread(g_hinst, 0);
ExitProc();
}