615 lines
17 KiB
C
615 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"
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* 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%x) 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
|
|
{
|
|
AssertF(pchid->hdev != INVALID_HANDLE_VALUE);
|
|
|
|
_CancelIO(pchid->hdev);
|
|
CloseHandle(pchid->hdev);
|
|
pchid->hdev = INVALID_HANDLE_VALUE;
|
|
|
|
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);
|
|
}
|
|
|