diff -r 453dfc402455 -r 0aa8cc770c8a localconnectivityservice/generichid/src/hidparser.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/localconnectivityservice/generichid/src/hidparser.cpp Tue Aug 31 16:03:15 2010 +0300 @@ -0,0 +1,1941 @@ +/* +* Copyright (c) 2004-2007 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: HID parser implementation +* +*/ + + +// ---------------------------------------------------------------------- + +// References: +// +// [1] USB Device Class Definition for Human Interface Devices (HID), +// Firmware Specification, Version 1.11, USB Implementers' Forum, +// June 2001 +// +// [2] HID Parser Error Codes (HID Parser Error Checking), Revision +// 1.2, USB Implementers' Forum, February 2000 +// +// [3] USB HID Usage Tables, Version 1.11, USB Implementers' Forum, +// June 2001 +// +// ---------------------------------------------------------------------- + +#include +#include +#include +#include + +#include "hidreportroot.h" +#include "hiditem.h" +#include "hidparser.h" +#include "debug.h" + + + + +// ---------------------------------------------------------------------- +/* +// Define PARSER_DEBUG to activate trace output for WINS debug builds: +#undef PARSER_DEBUG + +#if defined(PARSER_DEBUG) && defined(_DEBUG) && defined(__WINS__) +#define PDBG(a) a; +#define PDBG_ACTIVE +#else +#define PDBG(a) +#endif +*/ +#define PDBG_ACTIVE + +const TUint32 CParser::KLocalItemMask = + EUsageMin | EUsageMax | EUsageId | + EDesignatorIndex | EDesignatorMin | EDesignatorMax | + EStringIndex | EStringMin | EStringMax; + + +const TUint32 KMaxStandardType = 0x06; +const TUint32 KMinVendorType = 0x80; +const TUint32 KMaxVendorType = 0xFF; +const TUint32 KMaxUsagePage = 0xffff; +const TUint32 KUnitData = 0x0f; + +const TUint32 CParser::KUnusedLocalItemsMask = KLocalItemMask & ~EUsageId; + +const TUint32 KInputReservedBitsMask = 0xffffff00; +const TUint32 KOutputReservedBitsMask = 0xffffff00; +const TUint32 KFeatureReservedBitsMask = 0xffffff00; +const TUint32 KUnitReservedBitsMask = 0xf0000000; + +const TInt KConstantFlag = 1<<0; // Constant (1) or Data (0) +const TInt KVariableFlag = 1<<1; // Array (0) or Variable (1) +const TInt KNullStateFlag = 1<<6; + +const TInt KExtendedDataSize = 4; // 32-bit extended data size +const TInt KExtendedDataShift = 16; // 16 bit shift if extended usage page is used +const TInt KMaxReportIDMax = 255; +const TInt K32Bit = 32; + +const TUint K32BitFirstBitOn = 1u<<31; +const TInt KUnitSystemMin = 5; +const TInt KUnitSystem15 = 15; + +const TUint32 CParser::KMandatoryItemMask = EUsagePage | + ELogicalMin | ELogicalMax | EReportSize | EReportCount; + +const TUint32 CParser::KReportItemMask = EInputReport | + EOutputReport | EFeatureReport; + + +// Reserved values as per the HUT document 1.11, [3]: +// This ranges are reserverd in future use. + +const TInt KReservedUsage = 0x0e; +const TInt KReservedUsageRange1Min = 0x11; +const TInt KReservedUsageRange1Max = 0x13; +const TInt KReservedUsageRange2Min = 0x15; +const TInt KReservedUsageRange2Max = 0x3f; +const TInt KReservedUsageRange3Min = 0x41; +const TInt KReservedUsageRange3Max = 0x7f; +const TInt KReservedUsageRange4Min = 0x88; +const TInt KReservedUsageRange4Max = 0x8b; +const TInt KReservedUsageRange5Min = 0x92; +const TInt KReservedUsageRange5Max = 0xfeff; + +// ======== MEMBER FUNCTIONS ======== + +// --------------------------------------------------------------------------- +// NewLC() +// --------------------------------------------------------------------------- +// +EXPORT_C CParser* CParser::NewLC() + { + CParser* self = new (ELeave) CParser; + CleanupStack::PushL(self); + self->ConstructL(); + return self; + } + +// --------------------------------------------------------------------------- +// NewL() +// --------------------------------------------------------------------------- +// +EXPORT_C CParser* CParser::NewL() + { + CParser* self = NewLC(); + CleanupStack::Pop(); + return self; + } + +// --------------------------------------------------------------------------- +// ConstructL() +// --------------------------------------------------------------------------- +// +void CParser::ConstructL() + { + TRACE_FUNC_THIS + iLocal = CField::NewL(); + } + +// --------------------------------------------------------------------------- +// Constructor +// --------------------------------------------------------------------------- +// +CParser::CParser(): + iFieldCount(0) + { + TRACE_FUNC_THIS + } + +// --------------------------------------------------------------------------- +// Destructor +// --------------------------------------------------------------------------- +// +CParser:: ~CParser() + { + TRACE_FUNC_THIS + + // Free all RArray storage: + iGlobalStack.Reset(); + + + // Although iCollectionStack is an RPointerArray, we aren't doing + // a ResetAndDestroy() here, as all the collections are owned by + // the report root object, iReportRoot: + iCollectionStack.Reset(); + + delete iReportRoot; + delete iLocal; + } + +// --------------------------------------------------------------------------- +// CreateCollectionL +// --------------------------------------------------------------------------- +// +TInt CParser::CreateCollectionL(TUint32 aType) + { + TInt err = CheckForCollectionErrors(aType); + + if (err == KErrNone) + { + CCollection* collection = Collection()->AddCollectionL(); // Created collection added + // Collection's collection array + collection->SetType(aType); + collection->SetUsagePage(iGlobal.iUsagePage); + collection->SetUsage(iLocal->LastUsage()); + PushCollectionL(collection); + } + return err; + } + +// --------------------------------------------------------------------------- +// CheckForMainErrors() +// --------------------------------------------------------------------------- +// +TInt CParser::CheckForMainErrors() + { + if ( ( iItemsDefined & ELogicalMin ) && ( iItemsDefined & ELogicalMax ) ) + { + if ( iGlobal.iLogicalMin > iGlobal.iLogicalMax ) + { + IssueWarning( ELogicalMinExceedsMax ); + } + } + if ( iItemsDefined & (EPhysicalMin | EPhysicalMax ) ) + { + if ( !( iItemsDefined & EPhysicalMax ) ) + { + return ELonelyPhysicalMin; + } + if (!( iItemsDefined & EPhysicalMin )) + { + return ELonelyPhysicalMax; + } + + if ( iGlobal.iPhysicalMin > iGlobal.iPhysicalMax ) + { + IssueWarning(EPhysicalMinExceedsMax); + } + } + return KErrNone; + } + +// --------------------------------------------------------------------------- +// CheckForCollectionErrors() +// --------------------------------------------------------------------------- +// +TInt CParser::CheckForCollectionErrors(TUint32 aType) + { + if (iCollectionStack.Count() == 0 ) + { + return ENoCollectionToCheck; + } + + if (aType > KMaxStandardType) + { + if ((aType < KMinVendorType) || (aType > KMaxVendorType)) + { + IssueWarning( ECollectionTypeUnknownReserved ); + } + } + + if ( iItemsDefined & KUnusedLocalItemsMask ) + { + IssueWarning( ECollectionLocalUnused ); + } + + TInt numUsages = iLocal->UsageCount(); + + if ( numUsages > 1 ) + { + // Only a single usage value can be associated with a collection: + IssueWarning( ECollectionLocalUnused ); + } + + if ( numUsages == 0 ) + { + // A usage tag must be associated with a collection (see [1], + // Section 6.2.2.6): + IssueWarning( ECollectionHasNoUsage ); + } + + if ( !( iItemsDefined & EUsagePage ) ) + { + // A usage page must be associated with a collection (see [1], + // Section 6.2.2.6): + IssueWarning( ECollectionHasNoUsagePage ); + } + + if (( aType == CCollection::EApplication ) && ( iItemsDefined & EDelimiter )) + { + // Delimiters can't be used when defining usages that apply to + // Application Collections ([1], Section 6.2.2.8): + IssueWarning(EApplicationHasDelimiter); + + // It is an error to declare a delimiter for a top-level + // application collection, [2]: + if (iCollectionStack.Count() == 1) + { + return EDelimiterAtTopLevel; + } + } + return CheckForMainErrors(); + } + + +// --------------------------------------------------------------------------- +// CheckForFieldErrors() +// --------------------------------------------------------------------------- +// +TInt CParser::CheckForFieldErrors(CField::TType aType, TUint32 aAttributes) + { + TInt ret = KErrNone; + ret = CheckMandatoryFieldErrors(aType, aAttributes); + if ( ret != KErrNone ) + { + return ret; + } + + const TInt KLimitsError[] = + { EInputMinExceedsMax, EOutputMinExceedsMax, EFeatureMinExceedsMax }; + + if ( iGlobal.iLogicalMin > iGlobal.iLogicalMax ) + { + return KLimitsError[aType]; + } + + if ( ( iItemsDefined & ( EPhysicalMin | EPhysicalMax ) ) + && ( iGlobal.iPhysicalMin > iGlobal.iPhysicalMax )) + { + return KLimitsError[aType]; + } + CheckLogicalMinAndMax( aAttributes ); + CheckFieldBitNeeded( aType, aAttributes ); + return CheckForMainErrors(); + } + + +// --------------------------------------------------------------------------- +// BitsToRepresentRange() +// --------------------------------------------------------------------------- +// +TInt CParser::BitsToRepresentRange(TInt aMin, TInt aMax) + { + // The number of bits required to represent all values in the + // range aMin to aMax inclusive. If the range is all positive + // then there is no sign bit, otherwise twos complement format is + // assumed. ([1], Section 6.2.2.7.) + + TInt bitsNeeded = 0; + + if (aMin != aMax) + { + TUint absVal = static_cast(Max(Abs(aMin), Abs(aMax))); + + bitsNeeded = K32Bit - NumberOfLeadingZeros(absVal); + + // If either are negative, we'll need space for the sign bit: + // + if ((aMax < 0) || (aMin < 0)) + { + bitsNeeded++; + + // However, 2s complement allows us to represent one extra + // negative number than positive, and so our calculation + // may be one bit over. Catch this with a special case: + // + if (bitsNeeded > 1) + { + TInt n = 1 << (bitsNeeded - 2); + if ((aMin == -n) && (aMax < n)) + { + bitsNeeded--; + } + } + } + } + + return bitsNeeded; + } + +// --------------------------------------------------------------------------- +// NumberOfLeadingZeros() +// --------------------------------------------------------------------------- +// +TInt CParser::NumberOfLeadingZeros(TUint32 aValue) + { + TInt count = 0; + + TUint32 pos = K32BitFirstBitOn; + while ((pos != 0) && ((aValue & pos) == 0)) + { + count++; + pos >>= 1; + } + + return count; + } + + +// --------------------------------------------------------------------------- +// CheckAllReportSizes() +// --------------------------------------------------------------------------- +// +TBool CParser::CheckAllReportSizes() const + { + // Final report sizes must be an integral number of bytes, [2]: + + TBool sizesOk = ETrue; + + for (TInt i=0; sizesOk && (iNumberOfReports()); ++i) + { + TInt bits = iReportRoot->ReportSize(i); + + if ((bits == 0) || ((bits % 8) != 0)) + { + sizesOk = EFalse; + } + } + return sizesOk; + } + + +// --------------------------------------------------------------------------- +// CreateFieldL() +// --------------------------------------------------------------------------- +// +TInt CParser::CreateFieldL(CField::TType aType, TUint32 aAttributes) + { + TInt err = CheckForFieldErrors( aType, aAttributes ); + //Microsoft Elite 2 keyboard HID bug fix + if ( err == EInputMissingItems && iGlobal.iUsagePage == 0x07 ) + if ( iLocal->UsageMin( ) == 0xe0 && iLocal->UsageMax( ) == 0xe7 ) + { + iGlobal.iLogicalMin = 0x0; + iGlobal.iLogicalMax = 0x1; + err = KErrNone; + } + else + { + iGlobal.iLogicalMin = 0x0; + err = KErrNone; + } + + if (err == KErrNone) + { + // Create a new field object: + CField* field = Collection()->AddFieldL( ); // Created field added + // to collection's field array + DumpStateTableL( field ); + field->SetType( aType ); + field->SetAttributes( aAttributes ); + + // Set the field offset to the current report size, and + // increase the report size by the size of this field: + if ( !iReportRoot ) + { + User::Leave(ENoReportRoot); + } + field->SetOffset( iReportRoot->ReportSize( field->ReportId( ), aType) ); + iReportRoot->IncrementReportSizeL( field->ReportId(), + aType, field->Count() * field->Size() ); + TRACE_INFO(_L("CParser::CreateFieldL Field added")); + if ( field->UsageCount() ) + { + iFieldCount++; + } + } + return err; + } + +// --------------------------------------------------------------------------- +// DumpStateTableL() +// --------------------------------------------------------------------------- +// +void CParser::DumpStateTableL(CField *aField) const + { + TRACE_INFO((_L("DumpStateTableL(0x%08x)\n"), aField)); + + // Copy global state: + // + iGlobal.Populate(aField); + + // Copy local state: + // + aField->SetUsageRange( iLocal->UsageMin(), iLocal->UsageMax() ); + aField->SetDesignatorIndex( iLocal->DesignatorIndex() ); + aField->SetDesignatorRange( iLocal->DesignatorMin(), + iLocal->DesignatorMax() ); + aField->SetStringIndex( iLocal->StringIndex() ); + aField->SetStringRange( iLocal->StringMin(), iLocal->StringMax() ); + + // Copy usage list (local state) and calculate the usage range, if + // it hasn't already been explicitly specified: + // + if ( iLocal->UsageCount() > 0 ) + { + TInt minUsage, maxUsage; + minUsage = maxUsage = iLocal->Usage( 0 ); + + for (TInt i=0; iUsageCount(); ++i) + { + TInt value = iLocal->Usage( i ); + aField->AddUsageL( value ); + if ( value < minUsage ) + { + minUsage = value; + } + if ( value > maxUsage ) + { + maxUsage = value; + } + } + if ( (iItemsDefined & (EUsageMin | EUsageMax) ) == 0) + { + aField->SetUsageRange( minUsage, maxUsage ); + } + } + } + + +// --------------------------------------------------------------------------- +// Collection() +// --------------------------------------------------------------------------- +// +CCollection* CParser::Collection() + { + CCollection* lastcollection = NULL; + if ( iCollectionStack.Count( ) > 0 ) + { + lastcollection = iCollectionStack[ iCollectionStack.Count() - 1 ]; + } + return lastcollection; + } + +// --------------------------------------------------------------------------- +// PushCollectionL() +// --------------------------------------------------------------------------- +// +void CParser::PushCollectionL(const CCollection* aCollection) + { + User::LeaveIfError( iCollectionStack.Append( aCollection ) ); + } + +// --------------------------------------------------------------------------- +// PopCollection() +// --------------------------------------------------------------------------- +// +void CParser::PopCollection() + { + + if ( iCollectionStack.Count() > 0 ) + { + iCollectionStack.Remove( iCollectionStack.Count() - 1 ); + } + } + + +// --------------------------------------------------------------------------- +// IssueWarning() +// --------------------------------------------------------------------------- +// +void CParser::IssueWarning(TInt aHidWarningCode) + { + TRACE_ERROR((_L("Item %3d: Warning 0x%04x\n"), iItemNumber, aHidWarningCode)); + (void)aHidWarningCode; + } + +// --------------------------------------------------------------------------- +// ParseL() +// --------------------------------------------------------------------------- +// +EXPORT_C CReportRoot* CParser::ParseL(const TDesC8& aRawData) + { + ResetParserL(); + // Now we can parse the descriptor data: + const TInt length = aRawData.Length(); + TInt posn = 0; + TRACE_INFO((_L("CParser::ParseL() Start parsing length %d"), length)); + while ((posn < length) && (iErrorCode == 0)) + { + iItemNumber++; + TRACE_INFO((_L("posn is %d"), posn)); + + TItem item(aRawData.Right(length-posn)); + +#ifdef PDBG_ACTIVE + TRACE_INFO((_L("Item: size %d, tag %d, type %d\n"), + item.DataSize(), item.Tag(), item.Type())); + for (TInt i=0; i length) + { + iErrorCode = EInvalidItemLength; + } + } + + PopCollection(); + + // PUSH without POP: + CheckParseErrors(); + + iGlobalStack.Reset(); + iCollectionStack.Reset(); + + TRACE_INFO((_L("CParser::ParseL() error code is %d"), iErrorCode)); + // Finished, transfer ownership to caller: + CReportRoot* reportRoot = iReportRoot; + iReportRoot = 0; + return reportRoot; + } + +// --------------------------------------------------------------------------- +// CParser::FieldCount() +// --------------------------------------------------------------------------- +// +TInt CParser::FieldCount() + { + return iFieldCount; + } + +// --------------------------------------------------------------------------- +// CParser::MainItemL() +// --------------------------------------------------------------------------- +// +TInt CParser::MainItemL(const TItem& aItem) + { + TInt retVal = 0; + + switch (aItem.Tag()) + { + case EMainInput: + retVal = HandleMainInputTagL( aItem ); + break; + + case EMainOutput: + retVal = HandleMainOutputTagL( aItem ); + break; + + case EMainFeature: + retVal = HandleMainFeatureL( aItem ); + break; + + case EMainCollection: + retVal = HandleMainCollectionL( aItem ); + break; + + case EMainEndCollection: + retVal = HandleMainEndCollection( aItem ); + break; + default: + TRACE_ERROR(_L("Error: unknown main item\n")); + retVal = EUnknownItem; + break; + } + + // All main items cause local state to be cleared: + ClearLocalState(); + + // For checking if global items declared more than once between + // main items: + iGlobalItemsDefined = 0; + + return retVal; + } +// --------------------------------------------------------------------------- +// ClearLocalState() +// --------------------------------------------------------------------------- +// +void CParser::ClearLocalState() + { + iLocal->ClearUsageList(); + iLocal->SetUsageRange(0, 0); + iLocal->SetStringIndex(0); + iLocal->SetStringRange(0, 0); + iLocal->SetDesignatorIndex(0); + iLocal->SetDesignatorRange(0, 0); + + iItemsDefined &= ~(KLocalItemMask | EDelimiter); + iLocalMultipleUse = EFalse; + } + +// --------------------------------------------------------------------------- +// GlobalItemL() +// --------------------------------------------------------------------------- +// +TInt CParser::GlobalItemL(const TItem& aItem) + { + TInt retVal = 0; + switch (aItem.Tag()) + { + case EGlobalReportId: + retVal = HandleGlobalReportId( aItem ); + break; + case EGlobalUsagePage: + retVal = HandleGlobalUsagePage( aItem ); + break; + case EGlobalLogicalMinimum: + retVal = HandleGlobalLogicalMinimum( aItem ); + break; + case EGlobalLogicalMaximum: + retVal = HandleGlobalLogicalMaximum( aItem ); + break; + case EGlobalPhysicalMinimum: + retVal = HandleGlobalPhysicalMinimum( aItem ); + break; + case EGlobalPhysicalMaximum: + retVal = HandleGlobalPhysicalMaximum( aItem ); + break; + case EGlobalUnit: + retVal = HandleGlobalUnit( aItem ); + break; + case EGlobalUnitExponent: + retVal = HandleGlobalUnitExponent( aItem ); + break; + case EGlobalReportSize: + retVal = HandleGlobalReportSize( aItem ); + break; + case EGlobalReportCount: + retVal = HandleGlobalReportCount(aItem ); + break; + case EGlobalPush: + retVal = HandleGlobalPushL( aItem ); + break; + case EGlobalPop: + retVal = HandleGlobalPop( aItem ); + break; + default: + TRACE_ERROR(_L("Error: unknown global item\n")); + retVal = EUnknownItem; + break; + } + + return retVal; + } + +// --------------------------------------------------------------------------- +// LocalItemL() +// --------------------------------------------------------------------------- +// +TInt CParser::LocalItemL(const TItem& aItem) + { + TInt retVal = 0; + switch (aItem.Tag()) + { + case ELocalUsage: + retVal = HandleLocalUsageL( aItem ); + break; + case ELocalUsageMinimum: + retVal = HandleLocalUsageMinimum( aItem ); + break; + case ELocalUsageMaximum: + retVal = HandleLocalUsageMaximum( aItem ); + break; + case ELocalDesignatorIndex: + retVal = HandleLocalDesignatorIndex( aItem ); + break; + case ELocalDesignatorMinimum: + retVal = HandleLocalDesignatorMinimum( aItem ); + break; + case ELocalDesignatorMaximum: + retVal = HandleLocalDesignatorMaximum( aItem ); + break; + case ELocalStringIndex: + retVal = HandleLocalStringIndex( aItem ); + break; + case ELocalStringMinimum: + retVal = HandleLocalStringMinimum( aItem ); + break; + case ELocalStringMaximum: + retVal = HandleLocalStringMaximum( aItem ); + break; + // "HID parsers must handle Delimiters however, the support + // for the alternative usages that they define is optional. + // Usages other than the first (most preferred) usage defined + // may not be made accessible by system software.", [1], + // Section 6.2.2.8. + // + // This parser only supports the first usage in a delimiter list. + case ELocalDelimiter: + retVal = HandleLocalDelimiter( aItem ); + break; + default: + TRACE_ERROR(_L("Error: unknown local item\n")); + retVal = EUnknownItem; + break; + } + return retVal; + } + +// --------------------------------------------------------------------------- +// IsReservedUsagePage() +// --------------------------------------------------------------------------- +// +TBool CParser::IsReservedUsagePage(TInt aId) + { + return (aId == KReservedUsage) || + ((aId >= KReservedUsageRange1Min ) && (aId <= KReservedUsageRange1Max)) || + ((aId >= KReservedUsageRange2Min ) && (aId <= KReservedUsageRange2Max)) || + ((aId >= KReservedUsageRange3Min) && (aId <= KReservedUsageRange3Max)) || + ((aId >= KReservedUsageRange4Min ) && (aId <= KReservedUsageRange4Max)) || + ((aId >= KReservedUsageRange5Min) && (aId <= KReservedUsageRange5Max)); + } + +// --------------------------------------------------------------------------- +// Populate() +// --------------------------------------------------------------------------- +// +void TParserGlobalState::Populate(CField *aField) const + { + aField->SetUsagePage(iUsagePage); + aField->SetReportId(iReportId); + aField->SetLogicalRange(iLogicalMin, iLogicalMax); + aField->SetSize(iSize); + aField->SetCount(iCount); + aField->SetUnit(iUnit); + aField->SetUnitExponent(iUnitExponent); + + // If the physical min and max are both zero, then the HID class + // document specifies that they should be assumed to be equal to + // the corresponding logical values ([1], Section 6.2.2.7): + // + if ((iPhysicalMin == 0) && (iPhysicalMax == 0)) + { + aField->SetPhysicalRange(iLogicalMin, iLogicalMax); + } + else + { + aField->SetPhysicalRange(iPhysicalMin, iPhysicalMax); + } + } + + +// --------------------------------------------------------------------------- +// TParserGlobalState() +// --------------------------------------------------------------------------- +// +TParserGlobalState::TParserGlobalState() + : iUsagePage(0), iLogicalMin(0), iLogicalMax(0), + iPhysicalMin(0), iPhysicalMax(0), iUnit(0), + iUnitExponent(0), iReportId(0), iSize(0), iCount(0) + { + // Nothing else to do + } + +// --------------------------------------------------------------------------- +// HandleMainInputTagL +// --------------------------------------------------------------------------- +// +TInt CParser::HandleMainInputTagL(const TItem& aItem) + { + // Section 6.2.2.4 of the HID class specification, [1], + // states that an Input item may have a data size of zero + // bytes: + // + // "In this case the value of each data bit for the item + // can be assumed to be zero. This is functionally + // identical to using a item tag that specifies a 4-byte + // data item followed by four zero bytes." + // + // For a data size of zero, TItem::Data() will return zero + // and so we will get the required behaviour. + TRACE_INFO((_L("Input %d\n"), aItem.Data())); + TInt retVal=0; + + iItemsDefined |= EInputReport; + + if ( aItem.Data() & KInputReservedBitsMask ) + { + IssueWarning( EInputReservedBitsNonZero ); + } + if ( iLocalMultipleUse ) + { + IssueWarning( EInputLocalMultipleUse ); + } + if ( iWithinDelimiter ) + { + retVal = EInputItemWithinDelimiter; + } + else + { + retVal = CreateFieldL( CField::EInput, aItem.Data() ); + } + return retVal; + } + +// --------------------------------------------------------------------------- +// HandleMainOutputTagL +// --------------------------------------------------------------------------- +// +TInt CParser::HandleMainOutputTagL(const TItem& aItem) + { + TRACE_INFO((_L("Output %d\n"), aItem.Data())); + TInt retVal=0; + iItemsDefined |= EOutputReport; + + if ( aItem.Data() & KOutputReservedBitsMask ) + { + IssueWarning( EOutputReservedBitsNonZero ); + } + + if ( iLocalMultipleUse ) + { + IssueWarning( EOutputLocalMultipleUse ); + } + + if ( iWithinDelimiter ) + { + retVal = EOutputItemWithinDelimiter; + } + else + { + retVal = CreateFieldL( CField::EOutput, aItem.Data() ); + } + return retVal; + } + +// --------------------------------------------------------------------------- +// HandleMainFeatureL +// --------------------------------------------------------------------------- +// +TInt CParser::HandleMainFeatureL( const TItem& aItem ) + { + TRACE_INFO((_L("Feature %d\n"), aItem.Data())); + TInt retVal=0; + iItemsDefined |= EFeatureReport; + + if ( aItem.Data() & KFeatureReservedBitsMask ) + { + IssueWarning(EFeatureReservedBitsNonZero ); + } + + if ( iLocalMultipleUse ) + { + IssueWarning( EFeatureLocalMultipleUse ); + } + + if ( iWithinDelimiter ) + { + retVal = EFeatureItemWithinDelimiter; + } + else + { + retVal = CreateFieldL( CField::EFeature, aItem.Data() ); + } + + return retVal; + } + +// --------------------------------------------------------------------------- +// HandleMainCollectionL +// --------------------------------------------------------------------------- +// +TInt CParser::HandleMainCollectionL( const TItem& aItem ) + { + TRACE_INFO((_L("Start collection %d\n"), aItem.Data())); + TInt retVal = 0; + + if ( iWithinDelimiter ) + { + retVal = EBeginCollectionWithinDelimiter; + } + else + { + // Application collections can only be declared at + // top-level: + if ((aItem.Data() == CCollection::EApplication) && + (iCollectionStack.Count() != 1)) + { + retVal = EApplicationCollectionLevel; + } + else + { + retVal = CreateCollectionL(aItem.Data()); + } + } + return retVal; + } + +// --------------------------------------------------------------------------- +// HandleMainEndCollection +// --------------------------------------------------------------------------- +// +TInt CParser::HandleMainEndCollection( const TItem& aItem ) + { + TRACE_INFO((_L("Start collection %d\n"), aItem.Data())); + TInt retVal = 0; + + if (aItem.DataSize() != 0) + { + IssueWarning(EEndCollectionHasData); + } + + if (iItemsDefined & KLocalItemMask) + { + IssueWarning(EEndCollectionLocalUnused); + } + + if (iCollectionStack.Count() > 1) + { + PopCollection(); + } + else + { + retVal = ENoMatchingBeginCollection; + } + + if (iWithinDelimiter) + { + retVal = EEndCollectionWithinDelimiter; + } + + return retVal; + } + +// --------------------------------------------------------------------------- +// HandleGlobalReportId +// --------------------------------------------------------------------------- +// +TInt CParser::HandleGlobalReportId( const TItem& aItem ) + { + TRACE_INFO((_L("Global report ID %d\n"), aItem.Data())); + TInt retVal = 0; + TUint reportId = aItem.Data(); + + if (reportId == 0) + { + retVal = EZeroReportId; + } + if (reportId > KMaxReportIDMax) + { + retVal = EReportIdTooBig; + } + + // If there are to be any report IDs specified at all, + // then a report ID must be defined before the first + // input, output or feature report: + // + if ((iGlobal.iReportId == 0) && (iItemsDefined & KReportItemMask)) + { + retVal = ELateReportId; + } + + // Report ID defined outside a top level collection (Microsoft + // restriction) + // + if (iCollectionStack.Count() == 1) + { + retVal = EReportIdOutsideTopLevel; + } + + // Same item shouldn't have been declared since last main item: + // + if (iGlobalItemsDefined & EReportId) + { + // This is an error according to [2], but as it isn't + // a critical problem, and as some real-world devices + // fail this check, we issue a warning instead: + IssueWarning(ERedundantGlobalItem); + } + iGlobalItemsDefined |= EReportId; + + iItemsDefined |= EReportId; + iGlobal.iReportId = reportId; + + return retVal; + } + +// --------------------------------------------------------------------------- +// HandleGlobalUsagePage +// --------------------------------------------------------------------------- +// +TInt CParser::HandleGlobalUsagePage( const TItem& aItem ) + { + TRACE_INFO((_L("Global usage page %d\n"), aItem.Data())); + TInt retVal = 0; + + iGlobal.iUsagePage = aItem.Data(); + + if (aItem.Data() == 0) + { + retVal = EZeroUsagePage; + } + + if (aItem.Data() > KMaxUsagePage) + { + retVal = EUsagePageOutOfRange; + } + + if (IsReservedUsagePage(aItem.Data())) + { + IssueWarning(EReservedUsagePage); + } + + iItemsDefined |= EUsagePage; + + if (iGlobalItemsDefined & EUsagePage) + { + retVal = ERedundantGlobalItem; + } + iGlobalItemsDefined |= EUsagePage; + + return retVal; + } + +// --------------------------------------------------------------------------- +// HandleGlobalLogicalMinimum +// --------------------------------------------------------------------------- +// +TInt CParser::HandleGlobalLogicalMinimum( const TItem& aItem ) + { + TRACE_INFO((_L("Global logical min %d\n"), aItem.SignedData())); + TInt retVal = 0; + iGlobal.iLogicalMin = aItem.SignedData(); + iItemsDefined |= ELogicalMin; + + if (iGlobalItemsDefined & ELogicalMin) + { + retVal = ERedundantGlobalItem; + } + iGlobalItemsDefined |= ELogicalMin; + + // "Until Physical Minimum and Physical Maximum are + // declared in a report descriptor they are assumed by the + // HID parser to be equal to Logical Minimum and Logical + // Maximum, respectively.", [1], Section 6.2.2.7. + // + if (!(iItemsDefined & EPhysicalMin)) + { + iGlobal.iPhysicalMin = aItem.SignedData(); + } + return retVal; + } + +// --------------------------------------------------------------------------- +// HandleGlobalLogicalMaximum +// --------------------------------------------------------------------------- +// +TInt CParser::HandleGlobalLogicalMaximum( const TItem& aItem ) + { + TRACE_INFO((_L("Global logical max %d\n"), aItem.SignedData())); + TInt retVal = 0; + + iGlobal.iLogicalMax = aItem.SignedData(); + if ( !(iItemsDefined & EPhysicalMax) ) + { + iGlobal.iPhysicalMax = aItem.SignedData(); + } + iItemsDefined |= ELogicalMax; + + if (iGlobalItemsDefined & ELogicalMax) + { + retVal = ERedundantGlobalItem; + } + iGlobalItemsDefined |= ELogicalMax; + return retVal; + } + +// --------------------------------------------------------------------------- +// HandleGlobalPhysicalMinimum +// --------------------------------------------------------------------------- +// +TInt CParser::HandleGlobalPhysicalMinimum( const TItem& aItem ) + { + TRACE_INFO((_L("Global physical min %d\n"), aItem.SignedData())); + TInt retVal = 0; + iGlobal.iPhysicalMin = aItem.SignedData(); + iItemsDefined |= EPhysicalMin; + + if (iGlobalItemsDefined & EPhysicalMin) + { + retVal = ERedundantGlobalItem; + } + iGlobalItemsDefined |= EPhysicalMin; + return retVal; + } + +// --------------------------------------------------------------------------- +// HandleGlobalPhysicalMaximum +// --------------------------------------------------------------------------- +// +TInt CParser::HandleGlobalPhysicalMaximum( const TItem& aItem ) + { + TRACE_INFO((_L("Global physical max %d\n"), aItem.SignedData())); + + TInt retVal = 0; + iGlobal.iPhysicalMax = aItem.SignedData(); + iItemsDefined |= EPhysicalMax; + + if ( iGlobalItemsDefined & EPhysicalMax ) + { + retVal = ERedundantGlobalItem; + } + iGlobalItemsDefined |= EPhysicalMax; + return retVal; + } + +// --------------------------------------------------------------------------- +// HandleGlobalUnit +// --------------------------------------------------------------------------- +// +TInt CParser::HandleGlobalUnit( const TItem& aItem ) + { + TRACE_INFO((_L("Global unit %d\n"), aItem.Data())); + TInt retVal = 0; + iGlobal.iUnit = aItem.Data(); + iItemsDefined |= EUnit; + + if (iGlobalItemsDefined & EUnit) + { + retVal = ERedundantGlobalItem; + } + iGlobalItemsDefined |= EUnit; + + TInt unitSystem = aItem.Data() & KUnitData; + if (((unitSystem >= KUnitSystemMin ) && (unitSystem != KUnitSystem15)) || + (aItem.Data() & KUnitReservedBitsMask )) + { + IssueWarning(EUnitReservedBitsNonZero); + } + return retVal; + } + +// --------------------------------------------------------------------------- +// HandleGlobalUnitExponent +// --------------------------------------------------------------------------- +// +TInt CParser::HandleGlobalUnitExponent( const TItem& aItem ) + { + TRACE_INFO((_L("Global unit exponent %d\n"), aItem.Data())); + TInt retVal = 0; + iGlobal.iUnitExponent = aItem.Data(); + iItemsDefined |= EUnitExponent; + + if (iGlobalItemsDefined & EUnitExponent) + { + retVal = ERedundantGlobalItem; + } + iGlobalItemsDefined |= EUnitExponent; + const TUint32 KReservedBits = ~0x0fUL; + if (aItem.Data() & KReservedBits) + { + IssueWarning( EExponentReservedBitsNonZero ); + } + return retVal; + } + +// --------------------------------------------------------------------------- +// HandleGlobalReportSize +// --------------------------------------------------------------------------- +// +TInt CParser::HandleGlobalReportSize( const TItem& aItem ) + { + TRACE_INFO((_L("Global report size %d\n"), aItem.Data())); + TInt retVal = 0; + iGlobal.iSize = aItem.Data(); + iItemsDefined |= EReportSize; + + if (iGlobalItemsDefined & EReportSize) + { + retVal = ERedundantGlobalItem; + } + iGlobalItemsDefined |= EReportSize; + return retVal; + } + +// --------------------------------------------------------------------------- +// HandleGlobalReportCount +// --------------------------------------------------------------------------- +// +TInt CParser::HandleGlobalReportCount( const TItem& aItem ) + { + TRACE_INFO((_L("Global report count %d\n"), aItem.Data())); + TInt retVal = 0; + iGlobal.iCount = aItem.Data(); + if (aItem.Data() == 0) + { + retVal = EZeroReportCount; + } + iItemsDefined |= EReportCount; + + if (iGlobalItemsDefined & EReportCount) + { + retVal = ERedundantGlobalItem; + } + iGlobalItemsDefined |= EReportCount; + return retVal; + } + +// --------------------------------------------------------------------------- +// HandleGlobalPushL +// --------------------------------------------------------------------------- +// +TInt CParser::HandleGlobalPushL( const TItem& aItem ) + { + TRACE_INFO(_L("Global push\n")); + TInt retVal = 0; + if (aItem.DataSize() != 0) + { + retVal = EPushHasData; + } + User::LeaveIfError(iGlobalStack.Append(iGlobal)); + return retVal; + } + +// --------------------------------------------------------------------------- +// HandleGlobalPop +// --------------------------------------------------------------------------- +// +TInt CParser::HandleGlobalPop( const TItem& aItem ) + { + TRACE_INFO(_L("Global pop\n")); + TInt retVal = 0; + if (aItem.DataSize() != 0) + { + retVal = EPopHasData; + } + + if (iGlobalStack.Count() > 0) + { + iGlobal = iGlobalStack[iGlobalStack.Count()-1]; + iGlobalStack.Remove(iGlobalStack.Count()-1); + } + else + { + retVal = EPopWithoutPush; + } + return retVal; + } + +// --------------------------------------------------------------------------- +// HandleLocalUsageL +// --------------------------------------------------------------------------- +// +TInt CParser::HandleLocalUsageL( const TItem& aItem ) + { + TRACE_INFO((_L("Local usage %d\n"), aItem.Data())); + TInt retVal = 0; + + if (aItem.DataSize() == KExtendedDataSize ) + { + // Extended (32-bit) usage: + TInt usagePage = (aItem.Data() >> KExtendedDataShift); + if (IsReservedUsagePage(usagePage)) + { + IssueWarning(EReservedUsagePage); + } + } + + if ((aItem.Data() & 0xffff) == 0) + { + IssueWarning(EZeroUsage); + } + + if (!iWithinDelimiter || (iAliasCount++ == 0)) + { + iLocal->AddUsageL(aItem.Data()); + iItemsDefined |= EUsageId; + } + return retVal; + } + +// --------------------------------------------------------------------------- +// HandleLocalUsageMinimum +// --------------------------------------------------------------------------- +// +TInt CParser::HandleLocalUsageMinimum( const TItem& aItem ) + { + TRACE_INFO((_L("Local usage min %d\n"), aItem.Data())); + TInt retVal = 0; + if (!iWithinDelimiter || (iAliasCountMin++ == 0)) + { + TInt usagePage = iGlobal.iUsagePage; + + if (aItem.DataSize() == KExtendedDataSize ) + { + // Extended usage specified. + usagePage = aItem.Data() >> KExtendedDataShift; + } + + if (iItemsDefined & EUsageMax) + { + TInt maxPage = + static_cast(iLocal->UsageMax()) >> KExtendedDataShift; + if (maxPage == 0) + { + maxPage = iGlobal.iUsagePage; + } + if (usagePage != maxPage) + { + retVal = EUsagePageMismatchMin; + } + } + iLocal->SetUsageMin(aItem.Data()); + } + if (iItemsDefined & EUsageMin) + { + iLocalMultipleUse = ETrue; + } + iItemsDefined |= EUsageMin; + return retVal; + } + +// --------------------------------------------------------------------------- +// HandleLocalUsageMaximum +// --------------------------------------------------------------------------- +// +TInt CParser::HandleLocalUsageMaximum( const TItem& aItem ) + { + TRACE_INFO((_L("Local usage max %d\n"), aItem.Data())); + + TInt retVal = 0; + + if (!iWithinDelimiter || (iAliasCountMax++ == 0)) + { + TInt usagePage = iGlobal.iUsagePage; + + if (aItem.DataSize() == KExtendedDataSize ) + { + // Extended usage specified. + usagePage = aItem.Data() >> KExtendedDataShift; + } + if (iItemsDefined & EUsageMin) + { + TInt minPage = + static_cast(iLocal->UsageMin()) >> KExtendedDataShift; + if (minPage == 0) + { + minPage = iGlobal.iUsagePage; + } + if (usagePage != minPage) + { + retVal = EUsagePageMismatchMax; + } + } + iLocal->SetUsageMax(aItem.Data()); + } + if (iItemsDefined & EUsageMax) + { + iLocalMultipleUse = ETrue; + } + iItemsDefined |= EUsageMax; + return retVal; + } + +// --------------------------------------------------------------------------- +// HandleLocalDesignatorIndex +// --------------------------------------------------------------------------- +// +TInt CParser::HandleLocalDesignatorIndex( const TItem& aItem ) + { + TRACE_INFO((_L("Local designator index %d\n"), + aItem.Data())); + TInt retVal = 0; + iLocal->SetDesignatorIndex(aItem.Data()); + if (iWithinDelimiter) + { + retVal = EInvalidItemWithinDelimiter; + } + if (iItemsDefined & EDesignatorIndex) + { + iLocalMultipleUse = ETrue; + } + iItemsDefined |= EDesignatorIndex; + return retVal; + } + +// --------------------------------------------------------------------------- +// HandleLocalDesignatorMinimum +// --------------------------------------------------------------------------- +// +TInt CParser::HandleLocalDesignatorMinimum( const TItem& aItem ) + { + TRACE_INFO((_L("Local designator min %d\n"), aItem.Data())); + TInt retVal = 0; + iLocal->SetDesignatorMin(aItem.Data()); + if ( iWithinDelimiter ) + { + retVal = EInvalidItemWithinDelimiter; + } + if (iItemsDefined & EDesignatorMin) + { + iLocalMultipleUse = ETrue; + } + iItemsDefined |= EDesignatorMin; + return retVal; + } + +// --------------------------------------------------------------------------- +// HandleLocalDesignatorMaximum +// --------------------------------------------------------------------------- +// +TInt CParser::HandleLocalDesignatorMaximum( const TItem& aItem ) + { + TRACE_INFO((_L("Local designator max %d\n"), aItem.Data())); + TInt retVal = 0; + iLocal->SetDesignatorMax(aItem.Data()); + if ( iWithinDelimiter ) + { + retVal = EInvalidItemWithinDelimiter; + } + if ( iItemsDefined & EDesignatorMax ) + { + iLocalMultipleUse = ETrue; + } + iItemsDefined |= EDesignatorMax; + return retVal; + } + +// --------------------------------------------------------------------------- +// HandleLocalStringIndex +// --------------------------------------------------------------------------- +// +TInt CParser::HandleLocalStringIndex( const TItem& aItem ) + { + TRACE_INFO((_L("Local string index %d\n"), aItem.Data())); + TInt retVal = 0; + iLocal->SetStringIndex(aItem.Data()); + if ( iItemsDefined & EStringIndex ) + { + iLocalMultipleUse = ETrue; + } + iItemsDefined |= EStringIndex; + if ( iWithinDelimiter ) + { + retVal = EInvalidItemWithinDelimiter; + } + return retVal; + } + +// --------------------------------------------------------------------------- +// HandleLocalStringMinimum +// --------------------------------------------------------------------------- +// +TInt CParser::HandleLocalStringMinimum( const TItem& aItem ) + { + TRACE_INFO((_L("Local string min %d\n"), + aItem.Data())); + TInt retVal = 0; + iLocal->SetStringMin(aItem.Data()); + if ( iItemsDefined & EStringMin) + { + iLocalMultipleUse = ETrue; + } + iItemsDefined |= EStringMin; + if (iWithinDelimiter) + { + retVal = EInvalidItemWithinDelimiter; + } + return retVal; + } + +// --------------------------------------------------------------------------- +// HandleLocalStringMaximum +// --------------------------------------------------------------------------- +// +TInt CParser::HandleLocalStringMaximum( const TItem& aItem ) + { + TRACE_INFO((_L("Local string max %d\n"), + aItem.Data())); + TInt retVal = 0; + iLocal->SetStringMax(aItem.Data()); + if ( iItemsDefined & EStringMax ) + { + iLocalMultipleUse = ETrue; + } + iItemsDefined |= EStringMax; + if ( iWithinDelimiter ) + { + retVal = EInvalidItemWithinDelimiter; + } + return retVal; + } + +// --------------------------------------------------------------------------- +// HandleLocalDelimiter +// --------------------------------------------------------------------------- +// +TInt CParser::HandleLocalDelimiter( const TItem& aItem ) + { + const TInt KBeginDelimiter = 1; + const TInt KEndDelimiter = 0; + TInt retVal = 0; + + switch ( aItem.Data() ) + { + case KBeginDelimiter: + if ( iWithinDelimiter ) + { + retVal = ENestedDelimiter; + } + // Delimiters can't be used when defining usages + // that apply to array items ([1], Section 6.2.2.8): + // + if ( Collection()->Type() == CCollection::ENamedArray ) + { + IssueWarning( EDelimiterWithinNamedArray ); + } + iWithinDelimiter = ETrue; + iAliasCount = 0; + iAliasCountMin = 0; + iAliasCountMax = 0; + break; + + case KEndDelimiter: + if ( !iWithinDelimiter ) + { + retVal = ELonelyDelimiter; + } + iWithinDelimiter = EFalse; + break; + + default: + retVal = EUnknownDelimiter; + TRACE_ERROR((_L("Error: Unknown delimiter type %d\n"), + aItem.Data())); + break; + } + return retVal; + } + +// --------------------------------------------------------------------------- +// CheckMandatoryFieldExistence +// --------------------------------------------------------------------------- +// +TInt CParser::CheckMandatoryFieldExistence( CField::TType aType, TUint32 aAttributes ) + { + TInt retVal = KErrNone; + + if ( ( ( iItemsDefined & KMandatoryItemMask ) != KMandatoryItemMask ) + && (!(aAttributes & KConstantFlag))) + { + if ( aType == CField::EInput ) + { + retVal = EInputMissingItems; + } + if ( aType == CField::EOutput ) + { + retVal = EOutputMissingItems; + } + if ( aType == CField::EFeature ) + { + retVal = EFeatureMissingItems; + } + } + return retVal; + } + +// --------------------------------------------------------------------------- +// CheckUsageMinAndMaxErrors +// --------------------------------------------------------------------------- +// +TInt CParser::CheckUsageMinAndMaxErrors() + { + TInt retVal = KErrNone; + + if ( iItemsDefined & ( EUsageMin | EUsageMax )) + { + if (!( iItemsDefined & EUsageMax )) + { + retVal = ELonelyUsageMin; + } + if (!( iItemsDefined & EUsageMin )) + { + retVal = ELonelyUsageMax; + } + if ( iLocal->UsageMin() > iLocal->UsageMax() ) + { + retVal = EUsageMinExceedsMax; + } + } + return retVal; + } + +// --------------------------------------------------------------------------- +// CheckDesignatorMinAndMaxErrors +// --------------------------------------------------------------------------- +// +TInt CParser::CheckDesignatorMinAndMaxErrors() + { + TInt retVal = KErrNone; + + if ( iItemsDefined & ( EDesignatorMin | EDesignatorMax )) + { + if ( !( iItemsDefined & EDesignatorMax ) ) + { + retVal = ELonelyDesignatorMin; + } + if ( !( iItemsDefined & EDesignatorMin ) ) + { + retVal = ELonelyDesignatorMax; + } + if ( iLocal->DesignatorMin( ) > iLocal->DesignatorMax( ) ) + { + retVal = EDesignatorMinExceedsMax; + } + } + return retVal; + } + +// --------------------------------------------------------------------------- +// CheckStringMinAndMaxErrors +// --------------------------------------------------------------------------- +// +TInt CParser::CheckStringMinAndMaxErrors() + { + TInt retVal = KErrNone; + + if (iItemsDefined & (EStringMin | EStringMax)) + { + if ( !( iItemsDefined & EStringMax ) ) + { + retVal = ELonelyStringMin; + } + if ( !( iItemsDefined & EStringMin ) ) + { + retVal = ELonelyStringMax; + } + if ( iLocal->StringMin( ) > iLocal->StringMax( ) ) + { + retVal = EStringMinExceedsMax; + } + } + return retVal; + } + + +// --------------------------------------------------------------------------- +// CheckStringMinAndMaxErrors +// --------------------------------------------------------------------------- +// +TInt CParser::CheckMandatoryFieldErrors( CField::TType aType, TUint32 aAttributes ) + { + TInt ret = KErrNone; + // Check for mandatory items: + ret = CheckMandatoryFieldExistence( aType, aAttributes ); + if ( ret != KErrNone ) + { + return ret; + } + ret = CheckUsageMinAndMaxErrors(); + if ( ret != KErrNone ) + { + return ret; + } + ret = CheckDesignatorMinAndMaxErrors(); + if ( ret != KErrNone ) + { + return ret; + } + ret = CheckStringMinAndMaxErrors(); + if ( ret != KErrNone ) + { + return ret; + } + return KErrNone; + } + +// --------------------------------------------------------------------------- +// CheckLogicalMinAndMax +// --------------------------------------------------------------------------- +// +void CParser::CheckLogicalMinAndMax( TUint32 aAttributes ) + { + // Logical minimum and maximum must match the number of usage + // values defined if the Array flag is set (Var=0). (Ignore this + // check for constant fields) + // + if ( !( aAttributes & KVariableFlag ) && ( ! ( aAttributes & KConstantFlag ) ) ) + { + // Logical minimum must equal 1: + // + if ( iGlobal.iLogicalMin != 1 ) + { + // This is an error according to [2], but we issue a + // warning instead, as many devices (including the + // Logitech diNovo keyboard) fail this check: + IssueWarning( ELogicalMinInvalidForArray ); + } + + // Logical maximum must equal the number of defined usages: + // + TInt numUsages = iLocal->UsageCount(); + if ( numUsages == 0 ) + { + numUsages = iLocal->UsageMax( ) - iLocal->UsageMin( ) + 1; + } + if ( iGlobal.iLogicalMax != numUsages ) + { + // Again, we issue a warning rather than an error: + IssueWarning( ELogicalMaxInvalidForArray ); + } + } + + } + +// --------------------------------------------------------------------------- +// CheckFieldBitNeeded +// --------------------------------------------------------------------------- +// +void CParser::CheckFieldBitNeeded( CField::TType aType, TUint32 aAttributes ) + { + // "The bit field declared by Report Size must be large enough to + // hold all values declared by Logical Minimum and Logical + // Maximum. This includes a sign bit if either are less than + // 0. Also if the Null flag is set then the field must be capable + // of reporting all values declared by Logical Minimum and Logical + // Maximum, and a null value.", [2] (footnote 5). + + TInt bitsNeeded = 0; + + if ( !( aAttributes & KConstantFlag ) ) + { + if ( aAttributes & KNullStateFlag ) + { + // The null state flag is set, so there needs to be at + // least one extra "out of range" value. This could be + // below the lowest value or above the highest, whichever + // will fit better: + bitsNeeded = Min( + BitsToRepresentRange(iGlobal.iLogicalMin - 1, + iGlobal.iLogicalMax), + BitsToRepresentRange(iGlobal.iLogicalMin, + iGlobal.iLogicalMax + 1)); + } + else + { + // No null state declared: + bitsNeeded = BitsToRepresentRange(iGlobal.iLogicalMin, + iGlobal.iLogicalMax); + } + } + + if ( iGlobal.iSize < bitsNeeded ) + { + // The Logitech diNovo is missing a Logical Min and Logical + // Max pair and so will trigger a range error here. As a + // workaround, we will treat this as a warning rather than + // a critical error: + const TInt KRangeError[] = + { EInputReportSize, EOutputReportSize, EFeatureReportSize }; + IssueWarning(KRangeError[aType]); + } + } + +// --------------------------------------------------------------------------- +// HandleItem +// --------------------------------------------------------------------------- +// +void CParser::HandleItemL( TItem& aItem ) + { + iErrorCode = EUnknownItem; + if (aItem.IsLocal()) + { + iErrorCode = LocalItemL(aItem); + } + else + { + // Not allowed non-local items within a delimiter pair: + // + if ( iWithinDelimiter ) + { + iErrorCode = EInvalidItemWithinDelimiter; + } + else + { + if ( aItem.IsMain() ) + { + iErrorCode = MainItemL(aItem); + } + + if ( aItem.IsGlobal() ) + { + iErrorCode = GlobalItemL(aItem); + } + if ( aItem.IsLong() ) + { + IssueWarning(ELongItemDefined); + iErrorCode = 0; + } + } + } + } + +// --------------------------------------------------------------------------- +// CheckParseErrors() +// --------------------------------------------------------------------------- +// +void CParser::CheckParseErrors() + { + if ( !iErrorCode && ( iGlobalStack.Count() > 0) ) + { + iErrorCode = EPushWithoutPop; + } + + // COLLECTION without END_COLLECTION: + if ( !iErrorCode && ( iCollectionStack.Count() != 0 ) ) + { + iErrorCode = ENoMatchingEndCollection; + } + + // DELIMITER(Open) without DELIMITER(Close): + if ( !iErrorCode && iWithinDelimiter ) + { + iErrorCode = ELonelyDelimiter; + } + + // Final size of all reports must be a multiple of eight bits: + if ( !CheckAllReportSizes() ) + { + IssueWarning( EReportMustBeEightBitMultiple ); + } + } + +// --------------------------------------------------------------------------- +// ResetParser +// --------------------------------------------------------------------------- +// +void CParser::ResetParserL() + { + // Create the root collection, which is the container for all + // other collections and fields: + // + delete iReportRoot; // may exist if there has been a Leave() + iReportRoot = 0; + iReportRoot = CReportRoot::NewL(); + iCollectionStack.Reset(); + PushCollectionL(iReportRoot); + + // Clear the error code and the warnings list: + // + iErrorCode = 0; + + // Reset the parser internal state: + // + iGlobal = TParserGlobalState(); + iGlobalStack.Reset(); + iWithinDelimiter = EFalse; + iItemsDefined = 0; + iGlobalItemsDefined = 0; + iItemNumber = 0; + ClearLocalState(); + } +