/***************************************************************************** * * 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. * * = : The operation completed successfully. * * : 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. * or . * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * = : The operation completed successfully * and the report is ready to be sent to the device. * * : 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 * 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; }