localconnectivityservice/generichid/src/hidparser.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 02 Feb 2010 00:45:58 +0200
changeset 1 388a17646e40
parent 0 c3e98f10fcf4
permissions -rw-r--r--
Revision: 201003 Kit: 201005

/*
* 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 <e32std.h>
#include <e32base.h>
#include <e32des8.h>
#include <e32svr.h>

#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<TUint>(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 && (i<iReportRoot->NumberOfReports()); ++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; i<iLocal->UsageCount(); ++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<item.DataSize(); ++i)
            {
            TRACE_INFO((_L("  Data[%d] = 0x%02x (%d)\n"),
                       i, item[i], item[i]))
            }
#endif

        HandleItemL( item );        
        posn += item.ItemSize();
        if (posn > 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<TUint32>(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<TUint32>(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();
    }