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

624 lines
21 KiB
C

/*****************************************************************************
*
* DIHidDat.c
*
* Copyright (c) 1996 Microsoft Corporation. All Rights Reserved.
*
* Abstract:
*
* HID data management.
*
* Contents:
*
* CHid_AddDeviceData
*
*****************************************************************************/
#include "dinputpr.h"
/*****************************************************************************
*
* The sqiffle for this file.
*
*****************************************************************************/
#define sqfl sqflHidDev
/*****************************************************************************
*
* @doc INTERNAL
*
* @method void | CHid | DelDeviceData |
*
* Remove an item of device data from the list.
*
* We grab the last item and slide it into place, updating
* the various pointers as we go.
*
* @parm PHIDREPORTINFO | phri |
*
* The HID report from which the item is being deleted.
*
* @parm int | idataDel |
*
* The data value being deleted.
*
* @parm HIDP_REPORT_TYPE | type |
*
* The report type we are mangling with.
*
*****************************************************************************/
void INTERNAL
CHid_DelDeviceData(PCHID this, PHIDREPORTINFO phri, int idataDel,
HIDP_REPORT_TYPE type)
{
DWORD dwBase = this->rgdwBase[type];
int iobjDel = phri->rgdata[idataDel].DataIndex + dwBase;
PHIDOBJCAPS phocDel = &this->rghoc[iobjDel];
int idataSrc;
SquirtSqflPtszV(sqflHidOutput,
TEXT("DelDeviceData(%d) - cdataUsed = %d, obj=%d"),
idataDel, phri->cdataUsed, iobjDel);
AssertF(idataDel >= 0);
AssertF(idataDel < phri->cdataUsed);
AssertF(phri->cdataUsed > 0);
/*
* Wipe out the item being deleted.
* Remember that the report needs to be rebuilt.
*/
AssertF(phocDel->idata == idataDel);
phocDel->idata = -1;
phri->fNeedClear = TRUE;
/*
* Slide the top item into its place.
*/
idataSrc = (int)--(phri->cdataUsed);
if (idataSrc > idataDel) {
int iobjSrc;
PHIDOBJCAPS phocSrc;
AssertF(idataSrc > 0 && idataSrc < phri->cdataMax);
iobjSrc = phri->rgdata[idataSrc].DataIndex + dwBase;
phocSrc = &this->rghoc[iobjSrc];
AssertF(phocSrc->idata == idataSrc);
phocSrc->idata = idataDel;
phri->rgdata[idataDel] = phri->rgdata[idataSrc];
}
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method void | CHid | ResetDeviceData |
*
* Clean out all old device data from the list.
*
* @parm PHIDREPORTINFO | phri |
*
* The HID report which should be reset.
*
* @parm HIDP_REPORT_TYPE | type |
*
* The report type we are mangling with.
*
*****************************************************************************/
void EXTERNAL
CHid_ResetDeviceData(PCHID this, PHIDREPORTINFO phri, HIDP_REPORT_TYPE type)
{
SquirtSqflPtszV(sqflHidOutput,
TEXT("ResetDeviceData(%d) - cdataUsed = %d"),
type, phri->cdataUsed);
if (phri->cdataUsed) {
int idata;
DWORD dwBase = this->rgdwBase[type];
phri->fNeedClear = TRUE;
for (idata = 0; idata < phri->cdataUsed; idata++) {
int iobj = phri->rgdata[idata].DataIndex + dwBase;
PHIDOBJCAPS phoc = &this->rghoc[iobj];
AssertF(phoc->idata == idata);
phoc->idata = -1;
}
phri->cdataUsed = 0;
}
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | CHid | AddDeviceData |
*
* Add (or replace) a piece of device data to the array.
*
* If we are removing a button, then we delete it, because
* the HID way of talking about a button is "If you don't
* talk about it, then it isn't set."
*
* @parm UINT | uiObj |
*
* The object being added.
*
* @parm DWORD | dwData |
*
* The data value to add.
*
* @returns
*
* Returns a COM error code. The following error codes are
* intended to be illustrative and not necessarily comprehensive.
*
* <c DI_OK> = <c S_OK>: The operation completed successfully.
*
* <c DIERR_REPORTFULL>: Too many items are set in the report.
* ISSUE-2001/03/29-timgill Need more return code clarification
*
*****************************************************************************/
HRESULT EXTERNAL
CHid_AddDeviceData(PCHID this, UINT uiObj, DWORD dwData)
{
HRESULT hres;
LPDIOBJECTDATAFORMAT podf;
AssertF(uiObj < this->df.dwNumObjs);
podf = &this->df.rgodf[uiObj];
if (podf->dwType & DIDFT_OUTPUT) {
PHIDOBJCAPS phoc = &this->rghoc[uiObj];
PHIDGROUPCAPS pcaps = phoc->pcaps;
PHIDREPORTINFO phri;
// 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
SquirtSqflPtszV(sqflHidOutput,
TEXT("CHid_AddDeviceData(%p, %d, %d) - type %d"),
this, uiObj, dwData, pcaps->type);
/*
* Decide if it's HidP_Output or HidP_Feature.
*/
AssertF(HidP_IsOutputLike(pcaps->type));
switch (pcaps->type) {
case HidP_Output: phri = &this->hriOut; break;
case HidP_Feature: phri = &this->hriFea; break;
default: AssertF(0); hres = E_FAIL; goto done;
}
AssertF(phri->cdataUsed <= phri->cdataMax);
if (phoc->idata == -1) {
SquirtSqflPtszV(sqflHidOutput,
TEXT("CHid_AddDeviceData - no iData"));
} else {
AssertF(phoc->idata < phri->cdataUsed);
AssertF(uiObj == phri->rgdata[phoc->idata].DataIndex +
this->rgdwBase[pcaps->type]);
SquirtSqflPtszV(sqflHidOutput,
TEXT("CHid_AddDeviceData - iData = %d ")
TEXT("DataIndex = %d"),
phoc->idata,
phri->rgdata[phoc->idata].DataIndex);
}
phri->fChanged = TRUE;
if (pcaps->IsValue) {
/*
* Just swipe the value.
* The fallthrough code will handle this.
*/
} else {
/*
* If the button is being deleted, then delete it
* and that's all.
*/
if (dwData == 0) {
if (phoc->idata >= 0) {
CHid_DelDeviceData(this, phri, phoc->idata, pcaps->type);
AssertF(phoc->idata == -1);
} else {
SquirtSqflPtszV(sqflHidOutput,
TEXT("CHid_AddDeviceData - nop"));
}
hres = S_OK;
goto done;
} else {
dwData = TRUE; /* HidP_SetData requires this for buttons */
}
}
/*
* If there is not already a slot for this item, then
* find one.
*/
if (phoc->idata < 0) {
if (phri->cdataUsed < phri->cdataMax) {
USHORT DataIndex;
phoc->idata = phri->cdataUsed++;
DataIndex = (USHORT)(uiObj - this->rgdwBase[pcaps->type]);
phri->rgdata[phoc->idata].DataIndex = DataIndex;
SquirtSqflPtszV(sqflHidOutput,
TEXT("CHid_AddDeviceData - create iData = %d ")
TEXT("DataIndex = %d"),
phoc->idata,
DataIndex);
} else {
RPF("SendDeviceData: No room for more data");
hres = DIERR_REPORTFULL;
goto done;
}
}
AssertF(phri->cdataUsed <= phri->cdataMax);
AssertF(phoc->idata >= 0 && phoc->idata < phri->cdataUsed);
AssertF(uiObj == phri->rgdata[phoc->idata].DataIndex +
this->rgdwBase[pcaps->type]);
/*
* Here it comes... The entire purpose of this function
* is the following line of code... (Well, not the
* *entire* purpose, but 90% of it...)
*/
phri->rgdata[phoc->idata].RawValue = dwData;
SquirtSqflPtszV(sqflHidOutput,
TEXT("CHid_AddDeviceData - iData(%d) dwData = %d"),
phoc->idata, dwData);
hres = S_OK;
done:;
} else {
RPF("SendDeviceData: Object %08x is not DIDFT_OUTPUT",
podf->dwType);
hres = E_INVALIDARG;
}
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | CHid | SendHIDReport |
*
* Build up an output or feature report and send it.
* If the report has not changed, then do nothing.
*
* @parm PHIDREPORTINFO | phri |
*
* Describes the HID report we should build.
*
* @parm OUTPUTHIDREPORT | OutputHIDReport |
*
* Output a HID report to wherever it's supposed to go.
*
* @parm HIDP_REPORT_TYPE | type |
*
* The report type being sent.
* <c HidP_Output> or <c HidP_Feature>.
*
* @returns
*
* Returns a COM error code. The following error codes are
* intended to be illustrative and not necessarily comprehensive.
*
* <c DI_OK> = <c S_OK>: The operation completed successfully
* and the report is ready to be sent to the device.
*
* <c DIERR_REPORTFULL>: Too many items are set in the report.
*
* @cb HRESULT CALLBACK | SendHIDReportProc |
*
* An internal callback which takes a composed HID report
* and sends it in the appropriate manner to the device.
*
* @parm PCHID | this |
*
* The device in question.
*
* @parm PHIDREPORTINFO | phri |
*
* The report being sent.
*
*****************************************************************************/
STDMETHODIMP
CHid_SendHIDReport(PCHID this, PHIDREPORTINFO phri, HIDP_REPORT_TYPE type,
SENDHIDREPORT SendHIDReport)
{
HRESULT hres = S_OK;
DWORD cdata;
NTSTATUS stat;
if (phri->fChanged) {
if (phri->fNeedClear) {
ZeroMemory(phri->pvReport, phri->cbReport);
phri->fNeedClear = FALSE;
}
cdata = phri->cdataUsed;
stat = HidP_SetData(type, phri->rgdata, &cdata, this->ppd,
phri->pvReport, phri->cbReport);
if (SUCCEEDED(stat) && (int)cdata == phri->cdataUsed) {
if ( SUCCEEDED( hres = SendHIDReport(this, phri) ) ) {
phri->fChanged = FALSE;
}
} else if (stat == HIDP_STATUS_BUFFER_TOO_SMALL) {
hres = DIERR_REPORTFULL;
} else {
RPF("SendDeviceData: Unexpected HID failure");
hres = E_FAIL;
}
} else {
/* Vacuous success */
}
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method NTSTATUS | CHid | ParseData |
*
* Parse a single input report and set up the
* <e CHid.pvStage> buffer to contain the new device state.
*
* @parm HIDP_REPORT_TYPE | type |
*
* HID report type being parsed.
*
* @parm PHIDREPORTINFO | phri |
*
* Information that tells us how to parse the report.
*
*****************************************************************************/
NTSTATUS INTERNAL
CHid_ParseData(PCHID this, HIDP_REPORT_TYPE type, PHIDREPORTINFO phri)
{
NTSTATUS stat = E_FAIL;
/*
* Do this only if there are inputs at all. This avoids
* annoying boundary conditions.
*/
UCHAR uReportId;
ULONG cdataMax = phri->cdataMax;
if (cdataMax && phri->cbReport) {
uReportId = *(UCHAR*)phri->pvReport;
if( uReportId < this->wMaxReportId[type] &&
*(this->pEnableReportId[type] + uReportId) )
{
stat = HidP_GetData(type, phri->rgdata, &cdataMax,
this->ppd, phri->pvReport, phri->cbReport);
if (SUCCEEDED(stat)) {
ULONG idata;
/*
* If we successfully got stuff, then wipe out the old
* buttons and start with new ones.
*
* HID data parsing rules differ from buttons to axes.
* For buttons, the rule is that if it isn't in the
* report, then the button isn't presed.
*
* Compare axes, where the rule is that if it isn't
* in the report, then the value is unchanged.
*
* To avoid deleting buttons that are reported in reports
* other than the one just received we check for multiple
* reports during initialization and if necessary set up mask
* arrays for the buttons. The mask is an arrays of bytes of
* the same length as the button data, one for each report
* that contains any buttons. If the device only has one
* report there are no mask arrays so we can optimize by just
* zeroing all the buttons. If the device has multiple
* reports there is an array of pointers to the mask arrays,
* if a report has no buttons, the pointer is NULL so no
* further processing is required. For reports that do have
* buttons, each byte in the button data is ANDed with the
* corresponding byte in the mask so that only buttons in
* the received report are zeroed.
*/
if( this->rgpbButtonMasks == NULL )
{
/*
* Only one report so just zero all buttons
* This is the normal case so it is important that this
* be done as quickly as possible.
*/
ZeroMemory(pvAddPvCb(this->pvStage, this->ibButtonData),
this->cbButtonData);
}
else
{
if( this->rgpbButtonMasks[uReportId-1] != NULL )
{
/*
* ISSUE-2001/05/12-MarcAnd Could mask buttons faster
* If we do this often, we could consider doing masks
* with multiple bytes in each opperation.
*/
PBYTE pbMask;
PBYTE pbButtons;
PBYTE pbButtonEnd = pvAddPvCb(this->pvStage, this->ibButtonData + this->cbButtonData);
for( pbMask = this->rgpbButtonMasks[uReportId-1],
pbButtons = pvAddPvCb(this->pvStage, this->ibButtonData);
pbButtons < pbButtonEnd;
pbMask++, pbButtons++ )
{
*pbButtons &= *pbMask;
}
}
else
{
/*
* No buttons in this report
*/
}
}
for (idata = 0; idata < cdataMax; idata++) {
UINT uiObj;
PHIDGROUPCAPS pcaps;
/*
* Be careful and make sure that HID didn't
* give us anything with a bogus item index.
*
* ISSUE-2001/03/29-timgill Not Feature-friendly.
*/
AssertF(this->rgdwBase[HidP_Input] == 0);
SquirtSqflPtszV(sqfl | sqflTrace,
TEXT("HidP_GetData: %2d -> %d"),
phri->rgdata[idata].DataIndex,
phri->rgdata[idata].RawValue);
uiObj = this->rgdwBase[type] + phri->rgdata[idata].DataIndex;
if (uiObj < this->df.dwNumObjs &&
(pcaps = this->rghoc[uiObj].pcaps) &&
pcaps->type == type) {
LPDIOBJECTDATAFORMAT podf;
LONG lValue = (LONG)phri->rgdata[idata].RawValue;
/*
* Sign-extend the raw value if necessary.
*/
if (lValue & pcaps->lMask ) {
if( pcaps->IsSigned)
lValue |= pcaps->lMask;
else
lValue &= pcaps->lMask;
}
if (HidP_IsOutputLike(pcaps->type)) {
HRESULT hres;
hres = CHid_AddDeviceData(this, uiObj, lValue);
AssertF(SUCCEEDED(hres));
}
podf = &this->df.rgodf[uiObj];
if (!pcaps->IsValue) {
LPBYTE pb = pvAddPvCb(this->pvStage, podf->dwOfs);
AssertF(lValue);
*pb = 0x80;
} else {
LONG UNALIGNED *pl = pvAddPvCb(this->pvStage, podf->dwOfs);
// ISSUE-2001/03/29-timgill need to consider how logical/physical mapping can alter scaling
if (podf->dwType & DIDFT_RELAXIS) {
if (pcaps->usGranularity) {
lValue = -lValue * pcaps->usGranularity;
}
*pl += lValue;
} else if ( (podf->dwType & DIDFT_ABSAXIS)
#ifdef WINNT
|| ((podf->dwType & DIDFT_POV) && pcaps->IsPolledPOV)
#endif
) {
PJOYRANGECONVERT pjrc;
*pl = lValue;
/*
* Apply the ramp if any.
*/
pjrc = this->rghoc[uiObj].pjrc;
if( pjrc
&& !( this->pvi->fl & VIFL_RELATIVE ) )
{
CCal_CookRange(pjrc, pl);
}
} else if (podf->dwType & DIDFT_BUTTON) {
/*
* Current applications do not expect any values
* other than zero and 0x80. Just in case
* someone has implemented an analog button the
* way we had suggested, make sure we any value
* greater than or equal to half pressed reports
* 0x80 and anything else reports as zero.
* Note, out of range values default to zero.
*/
if( ( lValue <= pcaps->Logical.Max )
&& ( ( lValue - pcaps->Logical.Min ) >=
( ( ( pcaps->Logical.Max - pcaps->Logical.Min ) + 1 ) / 2 ) ) )
{
*((PBYTE)pl) = 0x80;
}
else
{
*((PBYTE)pl) = 0;
}
} else if (podf->dwType & DIDFT_POV) {
/*
* For (non-polled) POVs, an out of range value
* is a NULL aka centered. Otherwise work out
* the angle from the fraction of the circle.
*/
if (lValue < pcaps->Logical.Min ||
lValue > pcaps->Logical.Max) {
*pl = JOY_POVCENTERED;
} else {
lValue -= pcaps->Logical.Min;
*pl = lValue * pcaps->usGranularity;
}
}
}
} else {
SquirtSqflPtszV(sqfl | sqflTrace,
TEXT("HidP_GetData: Unable to use data element"));
}
}
stat = S_OK;
}
stat = S_OK;
}
} else {
stat = E_FAIL;
}
return stat;
}