994 lines
28 KiB
C
994 lines
28 KiB
C
|
|
/*****************************************************************************
|
|
* PidHid.c
|
|
*
|
|
* Copyright (c) 1999 Microsoft Corporation. All Rights Reserved.
|
|
*
|
|
* Abstract:
|
|
*
|
|
* HID utility routines for PID .
|
|
*
|
|
*****************************************************************************/
|
|
#include "PidPr.h"
|
|
|
|
#define sqfl ( sqflHid )
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* PID_GetReportId
|
|
*
|
|
* Obtain the HID report ID given the usage, usagePage and LinkCollection
|
|
*
|
|
* IDirectInputEffectDriver | ped |
|
|
*
|
|
* The effect driver interface
|
|
*
|
|
* PPIDREPORT | pPidReport |
|
|
*
|
|
* Address of PIDREPORT structure
|
|
*
|
|
* USHORT | uLinkCollection |
|
|
*
|
|
* LinkCollection ID
|
|
*
|
|
* OUT UCHAR * | pReportId |
|
|
*
|
|
* Report ID. Undefined if unsuccessful
|
|
*
|
|
* Returns:
|
|
*
|
|
* HRESULT
|
|
* Error code
|
|
*
|
|
*****************************************************************************/
|
|
STDMETHODIMP
|
|
PID_GetReportId
|
|
(
|
|
IDirectInputEffectDriver *ped,
|
|
PPIDREPORT pPidReport,
|
|
USHORT uLinkCollection,
|
|
UCHAR* pReportId
|
|
)
|
|
{
|
|
CPidDrv *this = (CPidDrv *)ped;
|
|
HRESULT hres = S_OK;
|
|
|
|
EnterProcI(PID_GetReportId, (_"xxxx", ped, pPidReport, pReportId));
|
|
|
|
if( SUCCEEDED(hres) )
|
|
{
|
|
HIDP_VALUE_CAPS ValCaps;
|
|
USHORT cAValCaps = 0x1;
|
|
USAGE Usage = DIGETUSAGE(pPidReport->rgPidUsage->dwUsage);
|
|
USAGE UsagePage = DIGETUSAGEPAGE(pPidReport->rgPidUsage->dwUsage);
|
|
|
|
hres = HidP_GetSpecificValueCaps
|
|
(
|
|
pPidReport->HidP_Type,
|
|
UsagePage,
|
|
uLinkCollection,
|
|
Usage,
|
|
&ValCaps,
|
|
&cAValCaps,
|
|
this->ppd
|
|
);
|
|
|
|
// If the report has no values, only buttons
|
|
if(hres == HIDP_STATUS_USAGE_NOT_FOUND )
|
|
{
|
|
// Guarded Laziness
|
|
CAssertF(cbX(HIDP_VALUE_CAPS) == cbX(HIDP_BUTTON_CAPS) );
|
|
CAssertF(FIELD_OFFSET(HIDP_VALUE_CAPS, ReportID) == FIELD_OFFSET(HIDP_BUTTON_CAPS, ReportID) );
|
|
|
|
hres = HidP_GetSpecificButtonCaps
|
|
(
|
|
pPidReport->HidP_Type,
|
|
UsagePage,
|
|
uLinkCollection,
|
|
0x0,
|
|
(PHIDP_BUTTON_CAPS)&ValCaps,
|
|
&cAValCaps,
|
|
this->ppd
|
|
);
|
|
}
|
|
|
|
if( SUCCEEDED(hres ) || ( hres == HIDP_STATUS_BUFFER_TOO_SMALL) )
|
|
{
|
|
(*pReportId) = ValCaps.ReportID;
|
|
hres = S_OK;
|
|
} else
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("%s: FAIL HidP_GetValCaps for CollectionId%d (%x %x:%s) "),
|
|
s_tszProc, uLinkCollection, UsagePage, Usage, PIDUSAGETXT(UsagePage,Usage) );
|
|
}
|
|
}
|
|
|
|
ExitOleProc();
|
|
|
|
return hres;
|
|
}
|
|
/*****************************************************************************
|
|
*
|
|
* PID_GetCollectionIndex
|
|
*
|
|
* Obtain the collection index for collection usage page & usage.
|
|
*
|
|
*
|
|
* The external joystick number being addressed.
|
|
*
|
|
* dwEffect
|
|
*
|
|
* The effect to be queried.
|
|
*
|
|
* pdwStatus
|
|
*
|
|
* Receives the effect status in the form of zero
|
|
* or more DIEGES_* flags.
|
|
*
|
|
* Returns:
|
|
* Collection Index ( 0 .. NumberLinkCollectionNodes -1 ) on success
|
|
* 0x0 on failure
|
|
*
|
|
*****************************************************************************/
|
|
STDMETHODIMP
|
|
PID_GetLinkCollectionIndex
|
|
(
|
|
IDirectInputEffectDriver *ped,
|
|
USAGE UsagePage,
|
|
USAGE Collection,
|
|
USHORT Parent,
|
|
PUSHORT puLinkCollection )
|
|
{
|
|
CPidDrv *this = (CPidDrv *)ped;
|
|
USHORT indx;
|
|
HRESULT hres;
|
|
PHIDP_LINK_COLLECTION_NODE pLinkCollection;
|
|
|
|
EnterProcI(PID_GetLinkCollectionIndex, (_"xxxxx", this, UsagePage, Collection, Parent, puLinkCollection));
|
|
hres = DIERR_PID_USAGENOTFOUND;
|
|
|
|
*puLinkCollection = 0x0;
|
|
for(indx = 0x0, pLinkCollection = this->pLinkCollection;
|
|
indx < this->caps.NumberLinkCollectionNodes;
|
|
indx++, pLinkCollection++ )
|
|
{
|
|
|
|
if( pLinkCollection->LinkUsagePage == UsagePage &&
|
|
pLinkCollection->LinkUsage == Collection )
|
|
{
|
|
if( Parent && Parent != pLinkCollection->Parent )
|
|
{
|
|
continue;
|
|
}
|
|
*puLinkCollection = indx;
|
|
hres = S_OK;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( FAILED(hres) )
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflVerbose,
|
|
TEXT("%s: FAIL No LinkCollection for (%x %x:%s) "),
|
|
s_tszProc, UsagePage, Collection, PIDUSAGETXT(UsagePage,Collection) );
|
|
}else
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflVerbose,
|
|
TEXT("%s: LinkCollection for (%x %x:%s)=%x "),
|
|
s_tszProc, UsagePage, Collection, PIDUSAGETXT(UsagePage,Collection), *puLinkCollection );
|
|
}
|
|
|
|
ExitBenignOleProc();
|
|
return (hres) ;
|
|
}
|
|
|
|
|
|
//Helper fn to tell us when we're dealing w/ an "absolute" usage, since they require special handling
|
|
BOOL PID_IsUsageAbsoluteLike
|
|
(
|
|
IDirectInputEffectDriver *ped,
|
|
USHORT Usage
|
|
)
|
|
{
|
|
//"Absolute-like" usages need special handling,
|
|
//since we can't simply translate data into logical units by scaling
|
|
//but need to calculate exponent, etc.
|
|
//and then use special procedure to set the values
|
|
//"Absolute" usages are all time usages as well as trigger button usages
|
|
if ((Usage == HID_USAGE_PID_DURATION) || (Usage ==HID_USAGE_PID_SAMPLE_PERIOD ) ||
|
|
(Usage == HID_USAGE_PID_TRIGGER_REPEAT_INTERVAL) || (Usage == HID_USAGE_PID_START_DELAY) ||
|
|
(Usage == HID_USAGE_PID_ATTACK_TIME ) ||(Usage == HID_USAGE_PID_FADE_TIME) ||
|
|
(Usage == HID_USAGE_PID_PERIOD) || (Usage == HID_USAGE_PID_TRIGGER_BUTTON))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//Helper fn to tell us when we're dealing w/ a magnitude that can be both positive and negative,
|
|
//since we have to scale those differently
|
|
BOOL PID_IsUsagePositiveNegative
|
|
(
|
|
IDirectInputEffectDriver *ped,
|
|
USHORT Usage,
|
|
USHORT LinkCollection
|
|
)
|
|
{
|
|
BOOL isPosNeg = FALSE;
|
|
//All the usages corresponding to the structures given by LONGs can be positive or negative.
|
|
//Exception is the direction / angle, which should already be scaled into the range 0 - 360 * DI_DEGREES,
|
|
//so should be treated as only positive.
|
|
//Another exception is DICONDITION.lDeadband, which is defined as a LONG, but our headers
|
|
//say it can only be in the range 0 to DI_FFNOMINALMAX.
|
|
if ((Usage == HID_USAGE_PID_CP_OFFSET) ||
|
|
(Usage == HID_USAGE_PID_POSITIVE_COEFFICIENT) || (Usage == HID_USAGE_PID_NEGATIVE_COEFFICIENT) ||
|
|
(Usage == HID_USAGE_PID_RAMP_START) ||(Usage == HID_USAGE_PID_RAMP_END) ||
|
|
(Usage == HID_USAGE_PID_OFFSET))
|
|
{
|
|
isPosNeg = TRUE;
|
|
}
|
|
|
|
//Magnitude of the constant force and the magnitude of the periodic force are defined to be the same thing,
|
|
//but only the constant force magnitude can be both positive and negative.
|
|
//To distinguish them, need to look at the collection.
|
|
//Get constant force's collection and compare.
|
|
if (Usage == HID_USAGE_PID_MAGNITUDE)
|
|
{
|
|
USHORT ConstCollection = 0x0;
|
|
PID_GetLinkCollectionIndex(ped, g_Constant.UsagePage, g_Constant.Collection, 0x0, &ConstCollection);
|
|
if (LinkCollection == ConstCollection)
|
|
{
|
|
isPosNeg = TRUE;
|
|
}
|
|
}
|
|
|
|
return isPosNeg;
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
PID_PackValue
|
|
(
|
|
IDirectInputEffectDriver *ped,
|
|
PPIDREPORT pPidReport,
|
|
USHORT LinkCollection,
|
|
PVOID pvData,
|
|
UINT cbData,
|
|
PCHAR pReport,
|
|
ULONG cbReport
|
|
)
|
|
{
|
|
CPidDrv *this = (CPidDrv *)ped;
|
|
HRESULT hres;
|
|
PPIDUSAGE pPidUsage;
|
|
UINT indx;
|
|
|
|
EnterProcI( PID_PackValue, (_"xxxxxxx", ped, pPidReport, LinkCollection, pvData, cbData, pReport, cbReport));
|
|
|
|
hres = S_OK;
|
|
// Loop over all data values in the PID Report
|
|
for(indx = 0x0, pPidUsage = pPidReport->rgPidUsage;
|
|
indx < pPidReport->cAPidUsage;
|
|
indx++, pPidUsage++ )
|
|
{
|
|
// Make sure the offsets are valid
|
|
if( pPidUsage->DataOffset < cbData )
|
|
{
|
|
LONG lValue;
|
|
NTSTATUS ntStat;
|
|
USHORT Usage = DIGETUSAGE(pPidUsage->dwUsage);
|
|
USHORT UsagePage = DIGETUSAGEPAGE(pPidUsage->dwUsage);
|
|
|
|
lValue = *((LONG*)((UCHAR*)pvData+pPidUsage->DataOffset));
|
|
|
|
ntStat = HidP_SetScaledUsageValue
|
|
(
|
|
pPidReport->HidP_Type,
|
|
UsagePage,
|
|
LinkCollection,
|
|
Usage,
|
|
lValue,
|
|
this->ppd,
|
|
pReport,
|
|
cbReport
|
|
);
|
|
|
|
if( FAILED(ntStat) )
|
|
{
|
|
// HidP_SetScaledUsageValue FAILED
|
|
|
|
SquirtSqflPtszV(sqfl | sqflBenign,
|
|
TEXT("%s: FAIL HidP_SetScaledUsageValue:0x%x for(%x,%x,%x:%s)=0x%x "),
|
|
s_tszProc, ntStat,
|
|
LinkCollection, UsagePage, Usage,
|
|
PIDUSAGETXT(UsagePage,Usage),
|
|
lValue );
|
|
|
|
// Try to set the unscaled value to get something that might make sense
|
|
if( ntStat != HIDP_STATUS_USAGE_NOT_FOUND )
|
|
{
|
|
lValue = -1;
|
|
// The range could be messed up.
|
|
ntStat = HidP_SetUsageValue
|
|
(
|
|
pPidReport->HidP_Type,
|
|
UsagePage,
|
|
LinkCollection,
|
|
Usage,
|
|
lValue,
|
|
this->ppd,
|
|
pReport,
|
|
cbReport
|
|
);
|
|
if(FAILED(ntStat) )
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflBenign,
|
|
TEXT("%s: FAIL HidP_SetUsageValue:0x%x for(%x,%x,%x:%s)=0x%x "),
|
|
s_tszProc, ntStat,
|
|
LinkCollection, UsagePage, Usage,
|
|
PIDUSAGETXT(UsagePage,Usage),
|
|
lValue );
|
|
}
|
|
}
|
|
|
|
} else
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflVerbose,
|
|
TEXT("%s: HidP_SetScaledUsageValue:0x%x for(%x,%x,%x:%s)=0x%x "),
|
|
s_tszProc, ntStat,
|
|
LinkCollection, UsagePage, Usage,
|
|
PIDUSAGETXT(UsagePage,Usage),
|
|
lValue );
|
|
}
|
|
} else
|
|
{
|
|
//SquirtSqflPtszV(sqfl | sqflBenign,
|
|
// TEXT("%s: FAIL Invalid Offset(%d), max(%d) "),
|
|
// s_tszProc, pPidUsage->DataOffset, cbData );
|
|
}
|
|
}
|
|
ExitOleProc();
|
|
return hres;
|
|
}
|
|
|
|
|
|
//blocking version -- used for creating a new effect or destroying an effect, and for custom forces
|
|
STDMETHODIMP
|
|
PID_SendReportBl
|
|
(
|
|
IDirectInputEffectDriver *ped,
|
|
PUCHAR pReport,
|
|
UINT cbReport,
|
|
HIDP_REPORT_TYPE HidP_Type
|
|
)
|
|
{
|
|
CPidDrv *this = (CPidDrv *)ped;
|
|
HRESULT hres;
|
|
|
|
EnterProcI( PID_SendReportBl, (_"xxxx", ped, pReport, cbReport, HidP_Type));
|
|
|
|
hres = S_OK;
|
|
if( HidP_Type == HidP_Output )
|
|
{
|
|
BOOL frc;
|
|
UINT cbWritten;
|
|
|
|
frc = WriteFile (this->hdev,
|
|
pReport,
|
|
cbReport,
|
|
&cbWritten,
|
|
NULL);
|
|
|
|
if( frc != TRUE || cbWritten != cbReport )
|
|
{
|
|
LONG lrc = GetLastError();
|
|
hres = hresLe(lrc);
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("%s: FAIL WriteFile():%d (cbWritten(0x%x) cbReport(0x%x) Le(0x%x)"),
|
|
s_tszProc, frc, cbWritten, cbReport, lrc );
|
|
|
|
}
|
|
} else if( HidP_Type == HidP_Feature )
|
|
{
|
|
hres = HidD_SetFeature
|
|
(this->hdev,
|
|
pReport,
|
|
cbReport
|
|
);
|
|
if(FAILED(hres) )
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("%s: FAIL SendD_Feature() hres:0x%x"),
|
|
s_tszProc, hres );
|
|
|
|
}
|
|
|
|
} else
|
|
{
|
|
hres = DIERR_PID_USAGENOTFOUND;
|
|
}
|
|
ExitOleProc();
|
|
return hres;
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
PID_SendReport
|
|
(
|
|
IDirectInputEffectDriver *ped,
|
|
PUCHAR pReport,
|
|
UINT cbReport,
|
|
HIDP_REPORT_TYPE HidP_Type,
|
|
BOOL bBlocking,
|
|
UINT blockNr,
|
|
UINT totalBlocks
|
|
)
|
|
{
|
|
CPidDrv *this = (CPidDrv *)ped;
|
|
HRESULT hres = S_OK;
|
|
|
|
EnterProcI( PID_SendReport, (_"xxxx", ped, pReport, cbReport, HidP_Type));
|
|
|
|
if (bBlocking == TRUE)
|
|
{
|
|
hres = PID_SendReportBl(ped, pReport, cbReport, HidP_Type);
|
|
}
|
|
else
|
|
{
|
|
AssertF(this->hThread != 0x0);
|
|
AssertF(this->hWrite != 0x0);
|
|
AssertF(this->hWriteComplete != 0x0);
|
|
|
|
//blockNr is 0-based.
|
|
AssertF(totalBlocks > 0);
|
|
AssertF(blockNr < totalBlocks);
|
|
|
|
if( HidP_Type == HidP_Output )
|
|
{
|
|
//WaitForMultipleObjects() till the completion event becomes set.
|
|
//we save each report into the appropriate place in the array.
|
|
//when we get all the reports, we set the event to signal to the other thread to write.
|
|
// Windows bug 627797 -- do not use INFINITE wait, so that we don't hang the app
|
|
//if smth goes wrong w/ the previous write, but instead use the blocking version.
|
|
DWORD dwWait = WaitForMultipleObjects(1, &this->hWriteComplete, FALSE, 1000);
|
|
if (dwWait == WAIT_OBJECT_0)
|
|
{
|
|
AssertF(this->dwWriteAttempt == 0);
|
|
//save the report data
|
|
ZeroMemory(this->pWriteReport[blockNr], this->cbWriteReport[blockNr]);
|
|
memcpy(this->pWriteReport[blockNr], pReport, cbReport);
|
|
this->cbWriteReport[blockNr] = (USHORT)cbReport;
|
|
if (blockNr == totalBlocks-1)
|
|
{
|
|
this->totalBlocks = totalBlocks;
|
|
this->blockNr = 0;
|
|
ResetEvent(this->hWriteComplete);
|
|
SetEvent(this->hWrite);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//The wait interval has expired, or an error has occured
|
|
RPF( TEXT("Waiting for the write completion event ended without the event being signaled, dwWait = %u"), dwWait);
|
|
//call the blocking version
|
|
hres = PID_SendReportBl(ped, pReport, cbReport, HidP_Type);
|
|
}
|
|
|
|
} else if( HidP_Type == HidP_Feature )
|
|
{
|
|
hres = HidD_SetFeature
|
|
(this->hdev,
|
|
pReport,
|
|
cbReport
|
|
);
|
|
if(FAILED(hres) )
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("%s: FAIL SendD_Feature() hres:0x%x"),
|
|
s_tszProc, hres );
|
|
|
|
}
|
|
|
|
} else
|
|
{
|
|
hres = DIERR_PID_USAGENOTFOUND;
|
|
}
|
|
}
|
|
|
|
ExitOleProc();
|
|
return hres;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
PID_ParseReport
|
|
(
|
|
IDirectInputEffectDriver *ped,
|
|
PPIDREPORT pPidReport,
|
|
USHORT LinkCollection,
|
|
PVOID pvData,
|
|
UINT cbData,
|
|
PCHAR pReport,
|
|
ULONG cbReport
|
|
)
|
|
{
|
|
CPidDrv *this = (CPidDrv *)ped;
|
|
HRESULT hres;
|
|
PPIDUSAGE pPidUsage;
|
|
UINT indx;
|
|
EnterProcI( PID_ParseReport, (_"xxxxxxx", ped, pPidReport, pvData, cbData, pReport, cbReport));
|
|
|
|
hres = S_OK;
|
|
// Loop over all data values in the PID Report
|
|
for(indx = 0x0, pPidUsage = pPidReport->rgPidUsage;
|
|
indx < pPidReport->cAPidUsage;
|
|
indx++, pPidUsage++ )
|
|
{
|
|
// Make sure the offsets are valid
|
|
if( pPidUsage->DataOffset < cbData )
|
|
{
|
|
LONG lValue;
|
|
NTSTATUS ntStat;
|
|
USHORT Usage = DIGETUSAGE(pPidUsage->dwUsage);
|
|
USHORT UsagePage = DIGETUSAGEPAGE(pPidUsage->dwUsage);
|
|
|
|
ntStat = HidP_GetScaledUsageValue
|
|
(
|
|
pPidReport->HidP_Type,
|
|
UsagePage,
|
|
LinkCollection,
|
|
Usage,
|
|
&lValue,
|
|
this->ppd,
|
|
pReport,
|
|
cbReport
|
|
);
|
|
|
|
if(SUCCEEDED(ntStat))
|
|
{
|
|
*((LONG*)((UCHAR*)pvData+pPidUsage->DataOffset)) = lValue;
|
|
} else
|
|
{
|
|
hres |= E_NOTIMPL;
|
|
SquirtSqflPtszV(sqfl | sqflBenign,
|
|
TEXT("%s: FAIL HidP_GetScaledUsageValue:0x%x for(%x,%x,%x:%s)"),
|
|
s_tszProc, ntStat,
|
|
LinkCollection, UsagePage, Usage,
|
|
PIDUSAGETXT(UsagePage,Usage) );
|
|
}
|
|
} else
|
|
{
|
|
|
|
SquirtSqflPtszV(sqfl | sqflBenign,
|
|
TEXT("%s: FAIL Invalid Offset(%d), max(%d) "),
|
|
s_tszProc, pPidUsage->DataOffset, cbData );
|
|
}
|
|
}
|
|
ExitBenignOleProc();
|
|
return hres;
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
PID_GetReport
|
|
(
|
|
IDirectInputEffectDriver *ped,
|
|
PPIDREPORT pPidReport,
|
|
USHORT LinkCollection,
|
|
PVOID pReport,
|
|
UINT cbReport
|
|
)
|
|
{
|
|
HRESULT hres = S_OK;
|
|
CPidDrv *this = (CPidDrv *)ped;
|
|
UCHAR ReportId;
|
|
EnterProcI( PID_GetReport, (_"xxxxx", ped, pPidReport, LinkCollection, pReport, cbReport));
|
|
|
|
if( SUCCEEDED(hres) )
|
|
{
|
|
hres = PID_GetReportId(ped, pPidReport, LinkCollection, &ReportId);
|
|
|
|
if(SUCCEEDED(hres) )
|
|
{
|
|
AssertF(pPidReport->HidP_Type == HidP_Feature);
|
|
|
|
if(SUCCEEDED(hres) )
|
|
{
|
|
ZeroBuf(pReport, cbReport);
|
|
|
|
/*
|
|
* The Win9x headers do not yet have use HidP_InitializeReportForID
|
|
* use MAXULONG_PTR to tell the header sets apart so that we can still build
|
|
*/
|
|
|
|
#ifdef WINNT
|
|
/*hres*=*/HidP_InitializeReportForID
|
|
(
|
|
pPidReport->HidP_Type, //ReportType,
|
|
ReportId, //ReportID,
|
|
this->ppd, //PreparsedData
|
|
pReport, //Report
|
|
cbReport //ReportLength
|
|
);
|
|
#else
|
|
(*(PUCHAR)pReport) = ReportId;
|
|
hres = S_OK;
|
|
#endif
|
|
if( FAILED(hres) )
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("%s: FAIL HidP_InitializeReportForId:0x%x for Type(%d) CollectionId%d ReportID%d "),
|
|
s_tszProc, hres, pPidReport->HidP_Type, LinkCollection, ReportId );
|
|
}
|
|
|
|
if( SUCCEEDED(hres)
|
|
&& pPidReport->HidP_Type == HidP_Feature )
|
|
{
|
|
BOOL frc;
|
|
frc = HidD_GetFeature
|
|
(
|
|
this->hdev, // HidDeviceObject,
|
|
pReport, // ReportBuffer,
|
|
cbReport //ReportBufferLength
|
|
);
|
|
|
|
if( frc != TRUE )
|
|
{
|
|
hres = DIERR_PID_USAGENOTFOUND;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ExitOleProc();
|
|
return(hres);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* PID_ComputeScalingFactors
|
|
*
|
|
* Dinput units for various parameters are well defined. The device may choose
|
|
* to implement the units that it is most comfortable with. This routine
|
|
* computes scaling factors that are to be used when scaling DINPUT parameters
|
|
* before they are send to the device.
|
|
*
|
|
* IDirectInputEffectDriver | ped |
|
|
*
|
|
* The effect driver interface
|
|
*
|
|
* PPIDREPORT | pPidReport |
|
|
*
|
|
* Address of PIDREPORT structure
|
|
*
|
|
* USHORT | uLinkCollection |
|
|
*
|
|
* LinkCollection ID
|
|
*
|
|
* IN OUT PVOID | pvData |
|
|
*
|
|
* Parameter data. On entry value is the nominal scale used by Dinput.
|
|
* For example: Angles: DI_DEGREES, DI_FFNOMINALMAX, DI_SECONDS
|
|
*
|
|
* IN UINT | cbData |
|
|
*
|
|
* Number of valid DWORDS in pvData
|
|
*
|
|
* Returns:
|
|
*
|
|
* HRESULT
|
|
* Error code
|
|
* E_NOTIMPL: Did not find any usage / usage Page
|
|
* DIERR_PID_INVALIDSCALING: Unsupported device scaling parameters.
|
|
* S_OK: Scaling value for at least one parameter was found
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
PID_ComputeScalingFactors
|
|
(
|
|
IDirectInputEffectDriver *ped,
|
|
PPIDREPORT pPidReport,
|
|
USHORT LinkCollection,
|
|
PVOID pvData,
|
|
UINT cbData,
|
|
PVOID pvOffset,
|
|
UINT cbOffset
|
|
)
|
|
{
|
|
HRESULT hres = E_NOTIMPL;
|
|
CPidDrv *this = (CPidDrv *)ped;
|
|
UINT indx;
|
|
PPIDUSAGE pPidUsage;
|
|
|
|
EnterProcI( PID_ComputeScalingFactors, (_"xxxxxxx", ped, pPidReport, LinkCollection, pvData, cbData, pvOffset, cbOffset));
|
|
|
|
// Loop over all data values in the PID Report
|
|
for(indx = 0x0, pPidUsage = pPidReport->rgPidUsage;
|
|
indx < pPidReport->cAPidUsage;
|
|
indx++, pPidUsage++ )
|
|
{
|
|
// Make sure the offsets are valid
|
|
if (( pPidUsage->DataOffset < cbData ) && (pPidUsage->DataOffset < cbOffset))
|
|
{
|
|
NTSTATUS ntStat;
|
|
HIDP_VALUE_CAPS ValCaps;
|
|
USHORT cAValCaps = 0x1;
|
|
|
|
USHORT Usage = DIGETUSAGE(pPidUsage->dwUsage);
|
|
USHORT UsagePage = DIGETUSAGEPAGE(pPidUsage->dwUsage);
|
|
PDWORD pdwValue;
|
|
PDWORD pdwOffset;
|
|
DWORD dwScale = 0x1;
|
|
DWORD dwOffset = 0x0;
|
|
|
|
pdwValue = ((DWORD*)((UCHAR*)pvData+pPidUsage->DataOffset));
|
|
pdwOffset = ((DWORD*)((UCHAR*)pvOffset+pPidUsage->DataOffset));
|
|
|
|
ntStat = HidP_GetSpecificValueCaps
|
|
(
|
|
pPidReport->HidP_Type,
|
|
UsagePage,
|
|
LinkCollection,
|
|
Usage,
|
|
&ValCaps,
|
|
&cAValCaps,
|
|
this->ppd
|
|
);
|
|
|
|
if(SUCCEEDED(ntStat))
|
|
{
|
|
//some units are "absolute" and thus don't need to be scaled to the limits.
|
|
//for them, we just find out the correct units
|
|
if (PID_IsUsageAbsoluteLike(ped, Usage))
|
|
{
|
|
if( ! ValCaps.Units )
|
|
{
|
|
SquirtSqflPtszV(sqfl | sqflVerbose,
|
|
TEXT("%s:No Units(%x,%x %x:%s) Max:%d Scale:%d "),
|
|
s_tszProc, LinkCollection, UsagePage, Usage, PIDUSAGETXT(UsagePage,Usage),
|
|
ValCaps.PhysicalMax, dwScale );
|
|
// No units, scaling exponent is default = 1
|
|
hres = S_FALSE;
|
|
} else
|
|
{
|
|
LONG UnitExp;
|
|
UnitExp = (LONG)ValCaps.UnitsExp ;
|
|
|
|
if( UnitExp > 0x0 )
|
|
{
|
|
RPF(TEXT("Driver does not support Units (%x,%x %x:%s) Exp:%d Max:%d"),
|
|
LinkCollection, UsagePage, Usage, PIDUSAGETXT(UsagePage,Usage), ValCaps.UnitsExp, ValCaps.PhysicalMax ) ;
|
|
hres = DIERR_PID_INVALIDSCALING;
|
|
}else
|
|
{
|
|
hres = S_OK;
|
|
}
|
|
|
|
if(SUCCEEDED(hres) )
|
|
{
|
|
dwScale = (*pdwValue);
|
|
for(; UnitExp; UnitExp++ )
|
|
{
|
|
dwScale /= 10;
|
|
}
|
|
|
|
if( dwScale == 0 )
|
|
{
|
|
RPF(TEXT("Driver does not support Units (%x,%x %x:%s) Exp:%d Max:%d"),
|
|
LinkCollection, UsagePage, Usage, PIDUSAGETXT(UsagePage,Usage), ValCaps.UnitsExp, ValCaps.PhysicalMax ) ;
|
|
dwScale = 0x1;
|
|
hres = DIERR_PID_INVALIDSCALING;
|
|
}else
|
|
{
|
|
hres = S_OK;
|
|
}
|
|
}
|
|
SquirtSqflPtszV(sqfl | sqflVerbose,
|
|
TEXT("%s: (%x,%x %x:%s) Exp%d Max:%d Scale:%d "),
|
|
s_tszProc, LinkCollection, UsagePage, Usage, PIDUSAGETXT(UsagePage,Usage),
|
|
ValCaps.UnitsExp, ValCaps.PhysicalMax, (*pdwValue) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//for everything else, get Physical and /or Logical Min/ Max
|
|
//From PID spec, doesn't have to have a Physical / Logical Min, but does have to have either Physical or Logical Max
|
|
if ((!ValCaps.PhysicalMax) && (!ValCaps.LogicalMax))
|
|
{
|
|
RPF(TEXT("Driver does not have either Physical Max or Logical Max for (%x,%x %x:%s)"),
|
|
LinkCollection, UsagePage, Usage, PIDUSAGETXT(UsagePage,Usage)) ;
|
|
hres = DIERR_PID_INVALIDSCALING;
|
|
}
|
|
else
|
|
{
|
|
//Compute the scaling value from either Physical or Logical Min/ Max and store it
|
|
int Scale = 0;
|
|
int Min = 0;
|
|
int Max = 0;
|
|
if (ValCaps.PhysicalMax)
|
|
{
|
|
Max = ValCaps.PhysicalMax;
|
|
if (ValCaps.PhysicalMin)
|
|
{
|
|
Min = ValCaps.PhysicalMin;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Max = ValCaps.LogicalMax;
|
|
if (ValCaps.LogicalMin)
|
|
{
|
|
Min = ValCaps.LogicalMin;
|
|
}
|
|
}
|
|
#ifdef DEBUG
|
|
//if Min/max are not in correct order, print a message so that we know if there are any problems w/ the forces
|
|
if (Min >= Max)
|
|
{
|
|
RPF(TEXT("Maximum of the device's range is %d, not bigger than minimum %d"), Max, Min);
|
|
}
|
|
#endif
|
|
//certain magnitudes can be both positive and negative -- for those, we need to know the device's offset
|
|
if (PID_IsUsagePositiveNegative(ped, Usage, LinkCollection))
|
|
{
|
|
|
|
Scale = (Max - Min)/2;
|
|
dwOffset = (Max + Min)/2;
|
|
|
|
}
|
|
//other magnitudes can only be positive
|
|
else
|
|
{
|
|
Scale = Max - Min;
|
|
dwOffset = Min;
|
|
}
|
|
//for angular usages, multiply by DI_FFNOMINALMAX and divide by 360 * DI_DEGREES
|
|
//we are doing this since later we will have no way of knowing that the values represent angles,
|
|
//and will thus divide all the values by DI_FFNOMINALMAX
|
|
if (*pdwValue == 360 * DI_DEGREES)
|
|
{
|
|
dwScale = MulDiv(Scale, DI_FFNOMINALMAX, (360 * DI_DEGREES));
|
|
}
|
|
else
|
|
{
|
|
dwScale = Scale;
|
|
}
|
|
hres = S_OK;
|
|
}
|
|
}
|
|
|
|
} else
|
|
{
|
|
// HidP_SetScaledUsageValue FAILED
|
|
SquirtSqflPtszV(sqfl | sqflBenign,
|
|
TEXT("%s: FAIL HidP_GetSpecificValueCaps:0x%x for(%x,%x,%x:%s)=0x%x "),
|
|
s_tszProc, ntStat,
|
|
LinkCollection, UsagePage, Usage,
|
|
PIDUSAGETXT(UsagePage,Usage),
|
|
dwScale );
|
|
}
|
|
|
|
(*pdwValue) = dwScale;
|
|
(*pdwOffset) = dwOffset;
|
|
} else
|
|
{
|
|
//SquirtSqflPtszV(sqfl | sqflVerbose,
|
|
// TEXT("%s: FAIL Invalid Offset(%d), max(%d) "),
|
|
// s_tszProc, pPidUsage->DataOffset, cbData );
|
|
}
|
|
}
|
|
|
|
ExitOleProc();
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* PID_ApplyScalingFactors
|
|
*
|
|
* Dinput units for various parameters are well defined. The device may choose
|
|
* to implement the units that it is most comfortable with. This routine
|
|
* apply scaling factors that are to be used when scaling DINPUT parameters
|
|
* before they are send to the device.
|
|
*
|
|
* IDirectInputEffectDriver | ped |
|
|
*
|
|
* The effect driver interface
|
|
*
|
|
* PPIDREPORT | pPidReport |
|
|
*
|
|
* Address of PIDREPORT structure
|
|
*
|
|
* IN PVOID | pvScale |
|
|
*
|
|
* Scaling values
|
|
*
|
|
* IN UINT | cbScale |
|
|
*
|
|
* Number of scaling values.
|
|
*
|
|
* IN OUT PVOID | pvData |
|
|
*
|
|
* Array of data values.
|
|
*
|
|
* IN UINT | cbData |
|
|
*
|
|
* Number of data values.
|
|
*
|
|
* Returns:
|
|
*
|
|
* HRESULT
|
|
* Error code
|
|
* E_NOTIMPL: Did not find any usage / usage Page
|
|
* DIERR_PID_INVALIDSCALING: Unsupported device scaling parameters.
|
|
* S_OK: Scaling value for at least one parameter was found
|
|
*
|
|
*****************************************************************************/
|
|
|
|
|
|
STDMETHODIMP
|
|
PID_ApplyScalingFactors
|
|
(
|
|
IDirectInputEffectDriver *ped,
|
|
PPIDREPORT pPidReport,
|
|
PVOID pvScale,
|
|
UINT cbScale,
|
|
PVOID pvOffset,
|
|
UINT cbOffset,
|
|
PVOID pvData,
|
|
UINT cbData
|
|
)
|
|
{
|
|
HRESULT hres = S_OK;
|
|
CPidDrv *this = (CPidDrv *)ped;
|
|
UINT indx;
|
|
PPIDUSAGE pPidUsage;
|
|
EnterProcI( PID_ApplyScalingFactors, (_"xxxxxxxx", ped, pPidReport, pvScale, cbScale, pvOffset, cbOffset, pvData, cbData));
|
|
// Loop over all data values in the PID Report
|
|
for(indx = 0x0, pPidUsage = pPidReport->rgPidUsage;
|
|
indx < pPidReport->cAPidUsage;
|
|
indx++, pPidUsage++ )
|
|
{
|
|
// Make sure we the offsets are valid
|
|
if( (pPidUsage->DataOffset < cbData) &&
|
|
(pPidUsage->DataOffset < cbScale) && ((pPidUsage->DataOffset < cbOffset) ))
|
|
{
|
|
PUINT pValue;
|
|
PUINT pScale;
|
|
PUINT pOffset;
|
|
|
|
pValue = ((PUINT)((UCHAR*)pvData +pPidUsage->DataOffset));
|
|
pScale = ((PUINT)((UCHAR*)pvScale +pPidUsage->DataOffset));
|
|
pOffset = ((PUINT)((UCHAR*)pvOffset +pPidUsage->DataOffset));
|
|
|
|
//"absolute"-like usages need special handling, because they don't need to be scaled to the max device values
|
|
if (PID_IsUsageAbsoluteLike(ped, DIGETUSAGE(pPidUsage->dwUsage)))
|
|
{
|
|
if( (*pScale) > 0x1 )
|
|
{
|
|
(*pValue) /= (*pScale) ;
|
|
}
|
|
}
|
|
//for everything else, do a calculation based on Logical or Physical Min/ Max
|
|
else
|
|
{
|
|
(int)(*pValue) = MulDiv((*pScale), (*pValue), DI_FFNOMINALMAX) + (*pOffset);
|
|
}
|
|
} else
|
|
{
|
|
//SquirtSqflPtszV(sqfl | sqflBenign,
|
|
// TEXT("%s: FAIL Invalid Offset(%d), max(%d) "),
|
|
// s_tszProc, pPidUsage->DataOffset, cbData );
|
|
}
|
|
}
|
|
|
|
ExitOleProc();
|
|
return hres;
|
|
}
|