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

621 lines
17 KiB
C

/*****************************************************************************
*
* DIEmH.c
*
* Copyright (c) 1996 Microsoft Corporation. All Rights Reserved.
*
* Abstract:
*
* Emulation module for HID. HID is always run at ring 3,
* so "emulation" is a bit of a misnomer.
*
* Contents:
*
* CEm_HID_CreateInstance
*
*****************************************************************************/
#include "dinputpr.h"
#ifdef HID_SUPPORT
/*****************************************************************************
*
* The sqiffle for this file.
*
*****************************************************************************/
#define sqfl sqflEm
/*****************************************************************************
*
* Forward declarations
*
* CEm_HID_ReadComplete and CEm_HID_IssueRead schedule each other
* back and forth.
*
*****************************************************************************/
void CALLBACK
CEm_HID_ReadComplete(DWORD dwError, DWORD cbRead, LPOVERLAPPED po);
/*****************************************************************************
*
* HID "emulation"
*
*****************************************************************************/
STDMETHODIMP CEm_HID_Acquire(PEM this, BOOL fAcquire);
/*****************************************************************************
*
* @doc INTERNAL
*
* @func BOOL | FakeCancelIO |
*
* Stub function which doesn't do anything but
* keeps us from crashing.
*
* @parm HANDLE | h |
*
* The handle whose I/O is supposed to be cancelled.
*
*****************************************************************************/
BOOL WINAPI
FakeCancelIO(HANDLE h)
{
AssertF(0);
return FALSE;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func BOOL | FakeTryEnterCriticalSection |
*
* We use TryEnterCriticalSection in DEBUG to detect deadlock
* If the function does not exist, just enter CritSection and report
* true. This compromises some debug functionality.
*
* @parm LPCRITICAL_SECTION | lpCriticalSection |
*
* Address of Critical Section to be entered.
*
*****************************************************************************/
#ifdef XDEBUG
BOOL WINAPI
FakeTryEnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
{
EnterCriticalSection(lpCriticalSection);
return TRUE;
}
#endif
/*****************************************************************************
*
* @doc INTERNAL
*
* @func void | CEm_HID_Hold |
*
* Place a hold on both the parent device and the
* emulation structure, so neither will go away while
* we aren't paying attention.
*
* @parm PCHID | this |
*
* The item to be held.
*
*****************************************************************************/
void INTERNAL
CEm_Hid_Hold(PCHID this)
{
CEm_AddRef(pemFromPvi(this->pvi));
Common_Hold(this);
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func void | CEm_HID_Unhold |
*
* Release the holds we placed via <f CEm_HID_Hold>.
*
* @parm PCHID | this |
*
* The item to be unheld.
*
*****************************************************************************/
void INTERNAL
CEm_Hid_Unhold(PCHID this)
{
CEm_Release(pemFromPvi(this->pvi));
Common_Unhold(this);
}
/*****************************************************************************
*
* @doc EXTERNAL
*
* @func BOOL | CEm_HID_IssueRead |
*
* Issue another read request.
*
* @parm PCHID | this |
*
* The device on which the read is to be issued.
*
* @returns
*
* Returns nonzero if the read was successfully issued.
*
*****************************************************************************/
BOOL EXTERNAL
CEm_HID_IssueRead(PCHID this)
{
BOOL fRc;
fRc = ReadFileEx(this->hdevEm, this->hriIn.pvReport,
this->hriIn.cbReport, &this->o,
CEm_HID_ReadComplete);
if(!fRc)
{
/*
* Couldn't issue read; force an unacquire.
*
* Unhold the device once, since the read loop is gone.
*/
// 7/18/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
SquirtSqflPtszV(sqfl | sqflError,
TEXT("IssueRead: Access to HID device(%p, handle=0x%p) lost le=0x%x!"),
this, this->hdevEm, GetLastError() );
DllEnterCrit();
ConfirmF(SUCCEEDED(GPA_DeletePtr(&g_plts->gpaHid, pemFromPvi(this->pvi))));
DllLeaveCrit();
CEm_ForceDeviceUnacquire(&this->ed,
(!(this->pvi->fl & VIFL_ACQUIRED)) ? FDUFL_UNPLUGGED : 0);
CEm_Hid_Unhold(this);
// 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
SquirtSqflPtszV(sqfl | sqflVerbose,
TEXT("Removed HID device(%p) from GPA "), this);
}
return fRc;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func void | CEm_HID_PrepareState |
*
* Prepare the staging area for a new device state
* by assuming that nothing has changed.
*
* @parm PCHID | this |
*
* The device on which a read has just completed.
*
*****************************************************************************/
void INLINE
CEm_HID_PrepareState(PCHID this)
{
/*
* Copy over everything...
*/
CopyMemory(this->pvStage, this->pvPhys, this->cbPhys);
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func void | CEm_HID_ReadComplete |
*
* APC function which is called when an I/O has completed.
*
* @parm DWORD | dwError |
*
* Error code, or zero on success.
*
* @parm DWORD | cbRead |
*
* Number of bytes actually read.
*
* @parm LPOVERLAPPED | po |
*
* I/O packet that completed.
*
*****************************************************************************/
void CALLBACK
CEm_HID_ReadComplete(DWORD dwError, DWORD cbRead, LPOVERLAPPED po)
{
PCHID this = pchidFromPo(po);
//EnterProc(Cem_HID_ReadComplete, (_"ddp", dwError, cbRead, po ));
/*
* Cannot own any critical sections because CEm_ForceDeviceUnacquire
* assumes that no critical sections are taken.
*/
AssertF(!CDIDev_InCrit(this->pvi->pdd));
AssertF(!DllInCrit());
/*
* Process the data.
*
* Note: We can get error STATUS_DEVICE_NOT_CONNECTED
* or ERROR_READ_FAULT if the device is unplugged.
*/
if(dwError == 0 &&
this->o.InternalHigh == this->caps.InputReportByteLength)
{
NTSTATUS stat;
CEm_HID_PrepareState(this);
stat = CHid_ParseData(this, HidP_Input, &this->hriIn);
if(SUCCEEDED(stat))
{
CEm_AddState(&this->ed, this->pvStage, GetTickCount());
}
CEm_HID_IssueRead(this);
} else
{
if(!dwError)
{
// 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
SquirtSqflPtszV(sqflError | sqfl,
TEXT("ReadComplete HID(%p) short read! Got %d wanted %d"),
this,
this->o.InternalHigh,
this->caps.InputReportByteLength);
} else
{
// 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
SquirtSqflPtszV(sqflError | sqfl,
TEXT("ReadComplete HID(%p) read failed! error=0x%08x "),
this, dwError);
}
DllEnterCrit();
ConfirmF(SUCCEEDED(GPA_DeletePtr(&g_plts->gpaHid, pemFromPvi(this->pvi))));
DllLeaveCrit();
CEm_ForceDeviceUnacquire(&this->ed,
(!(this->pvi->fl & VIFL_ACQUIRED)) ? FDUFL_UNPLUGGED : 0);
CEm_Hid_Unhold(this);
}
/*
* And wait for more data.
* If the read failed, then CEm_HID_IssueRead() will its Reference
*/
// CEm_HID_IssueRead(this);
//ExitProc();
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func void | CEm_HID_Sync |
*
* Kick off a read or kill the existing one.
*
* @parm PLLTHREADSTATE | plts |
*
* Thread hook state containing hook information to synchronize.
*
* @parm PEM | pem |
*
* Who is the poor victim?
*
*****************************************************************************/
void EXTERNAL
CEm_HID_Sync(PLLTHREADSTATE plts, PEM pem)
{
PCHID this;
EnterProc(CEm_HID_Sync, (_ "pp", plts, pem ));
this = pchidFromPem(pem);
AssertF(GPA_FindPtr(&plts->gpaHid, pem));
AssertF(this->pvi == &pem->vi);
AssertF(pem->ped == &this->ed);
/*
* Cannot own any critical sections because CEm_HID_IssueRead
* may result in a call to CEm_ForceDeviceUnacquire, which
* in turn assumes that no critical sections are taken.
*/
AssertF(!CDIDev_InCrit(this->pvi->pdd));
AssertF(!DllInCrit());
if( pem->vi.fl & VIFL_ACQUIRED )
{
AssertF(this->hdevEm == INVALID_HANDLE_VALUE);
/*
* Start reading.
*
* While underneath the device critical section, duplicate
* the handle so we can avoid race conditions with the
* main thread (when the main thread closes the handle,
* we need to keep our private version alive so we can
* clean it up nicely).
*/
/*
* Need to look again, in case the device has already
* been unacquired before we get a chance to synchronize
* with the main thread. This can happen, for example,
* if the app quickly does an Acquire/Unacquire without
* an intervening thread switch.
*/
AssertF(!CDIDev_InCrit(this->pvi->pdd));
//CDIDev_EnterCrit(this->pvi->pdd);
if(this->hdev != INVALID_HANDLE_VALUE)
{
HANDLE hProcessMe = GetCurrentProcess();
HANDLE hdevEm;
if(DuplicateHandle(hProcessMe, this->hdev,
hProcessMe, &hdevEm, GENERIC_READ,
0, 0))
{
this->hdevEm = hdevEm;
}
}
//CDIDev_LeaveCrit(this->pvi->pdd);
if(this->hdevEm != INVALID_HANDLE_VALUE)
{
/*
* On Win98, HidD_FlushQueue will fail if the underlying
* device is dead. Whereas on NT, it blindly succeeds.
* Therefore, we cannot trust the return value.
*/
HidD_FlushQueue(this->hdevEm);
}
/*
* Even if we have failed to duplicate the handle
* we still want to issue the read. A error in read
* will force the device to be unacquired
*/
CEm_HID_IssueRead(this);
// 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
SquirtSqflPtszV(sqfl | sqflVerbose,
TEXT(" StartReading(%p) "),
this);
} else
{
HANDLE hdev;
/*
* Stop reading. There is still another outstanding
* hold by the read loop, which will be cleaned up when
* the the I/O cancel is received.
*/
AssertF(this->hdevEm != INVALID_HANDLE_VALUE);
hdev = this->hdevEm;
this->hdevEm = INVALID_HANDLE_VALUE;
if(hdev != INVALID_HANDLE_VALUE)
{
/*
* We don't need to call CancelIo because we're closing
* the handle soon anyway. Which is good, because Memphis
* B#55771 prevents CancelIo from working on read-only
* handles (which we are).
*
*/
/* Need CancelIo on NT otherwise HID devices appear only on every
* consecutive plug in
*/
_CancelIO(hdev);
CloseHandle(hdev);
}
// 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
SquirtSqflPtszV(sqfl | sqflVerbose,
TEXT(" StopReading(%p) "),
this);
}
ExitProc();
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CEm_HID_Acquire |
*
* Acquire/unacquire a HID device.
*
* @parm PEM | pem |
*
* Device being acquired.
*
* @parm BOOL | fAcquire |
*
* Whether the device is being acquired or unacquired.
*
*****************************************************************************/
STDMETHODIMP
CEm_HID_Acquire(PEM pem, BOOL fAcquire)
{
HRESULT hres;
PLLTHREADSTATE plts;
PCHID pchid;
EnterProc(CEm_HID_Acquire, (_ "pu", pem, fAcquire));
AssertF(pem->dwSignature == CEM_SIGNATURE);
pchid = pchidFromPem(pem);
if( fAcquire )
{
pchid->hdev = CHid_OpenDevicePath(pchid, FILE_FLAG_OVERLAPPED);
if(pchid->hdev != INVALID_HANDLE_VALUE )
{
hres = S_OK;
} else
{
hres = DIERR_UNPLUGGED;
}
} else
{
HANDLE hdev;
AssertF(pchid->hdev != INVALID_HANDLE_VALUE);
hdev = pchid->hdev;
pchid->hdev = INVALID_HANDLE_VALUE;
_CancelIO(hdev);
CloseHandle(hdev);
hres = S_OK;
}
if( pchid->IsPolledInput )
{
hres = S_OK;
AssertF(pchid->hdevEm == INVALID_HANDLE_VALUE);
} else if( SUCCEEDED(hres) )
{
#ifdef USE_WM_INPUT
ResetEvent( g_hEventHid );
#endif
hres = CEm_GetWorkerThread(pem, &plts);
if(SUCCEEDED(hres) )
{
if(fAcquire )
{
/* Begin the I/O */
/*
* Must apply the hold before adding to the list
* to avoid a race condition where the worker thread
* unholds the pchid before we can hold it.
*
* The rule is that there is a hold to track each copy
* of the device on the gpaHid.
*/
CEm_Hid_Hold(pchid);
/*
* Add ourselves to the busy list, and wake up
* the worker thread to tell him to start paying attention.
*/
DllEnterCrit();
hres = GPA_Append(&plts->gpaHid, pem);
DllLeaveCrit();
// 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
SquirtSqflPtszV(sqfl | sqflVerbose,
TEXT("Added HID device(%p) to GPA "), pchid );
if(FAILED(hres))
{
CEm_Hid_Unhold(pchid);
}
NudgeWorkerThreadPem(plts, pem);
#ifdef USE_WM_INPUT
if( g_fRawInput) {
DWORD dwRc;
dwRc = WaitForSingleObject( g_hEventHid, INFINITE );
}
#endif
} else
{
HANDLE hdev;
hdev = pchid->hdevEm;
pchid->hdevEm = INVALID_HANDLE_VALUE;
if(hdev != INVALID_HANDLE_VALUE)
{
_CancelIO(hdev);
CloseHandle(hdev);
}
}
}
}
ExitOleProc();
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CEm_HID_CreateInstance |
*
* Create a HID thing.
*
* @parm PVXDDEVICEFORMAT | pdevf |
*
* What the object should look like.
*
* @parm PVXDINSTANCE * | ppviOut |
*
* The answer goes here.
*
*****************************************************************************/
HRESULT EXTERNAL
CEm_HID_CreateInstance(PVXDDEVICEFORMAT pdevf, PVXDINSTANCE *ppviOut)
{
PCHID pchid = (PCHID)pdevf->dwExtra;
PED ped = &pchid->ed;
AssertF(ped->pState == 0);
AssertF(ped->pDevType == 0);
*(PPV)&ped->pState = pchid->pvPhys; /* De-const */
ped->Acquire = CEm_HID_Acquire;
ped->cAcquire = -1;
ped->cbData = pdevf->cbData;
ped->cRef = 0x0;
return CEm_CreateInstance(pdevf, ppviOut, &pchid->ed);
}
#endif /* HID_SUPPORT */