381 lines
9.4 KiB
C
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();
|
||
|
}
|