/*
 * Copyright (c) 2008,2009 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:
 * Implementation of CCea861EdidParser class.
 *
 */

#include "cea861edidparser.h"
#include "trace.h"

// ======== LOCAL FUNCTIONS ========


// ======== MEMBER FUNCTIONS ========


// ---------------------------------------------------------------------------
// CCea861EdidParser::CCea861EdidParser
// ---------------------------------------------------------------------------
//
CCea861EdidParser::CCea861EdidParser() :
    iParsed( EFalse ),
    iAudioDataBlockSupported( EFalse ),
    iVideoDataBlockSupported( EFalse ),
    iVendorSpecificDataBlockSupported( EFalse ),
    iSpeakerAllocationDataBlockSupported( EFalse )
    {
    FUNC_LOG;
    }

// ---------------------------------------------------------------------------
// CEdidParserBase::ConstructL
// ---------------------------------------------------------------------------
//
void CCea861EdidParser::ConstructL( const TExtDataBlock& aData )
    {
    FUNC_LOG;

    iParsedInfo = CCea861ExtEdidInformation::NewL();
    ParseExtensionBlockL( aData );
    }

// ---------------------------------------------------------------------------
// CEdidParserBase::NewL
// ---------------------------------------------------------------------------
//
EXPORT_C CCea861EdidParser* CCea861EdidParser::NewL( const TExtDataBlock& aData )
    {
    FUNC_LOG;

    CCea861EdidParser* self = CCea861EdidParser::NewLC( aData );
    CleanupStack::Pop( self );
    return self;
    }

// ---------------------------------------------------------------------------
// CEdidParserBase::NewLC
// ---------------------------------------------------------------------------
//
EXPORT_C CCea861EdidParser* CCea861EdidParser::NewLC( const TExtDataBlock& aData )
    {
    FUNC_LOG;

    CCea861EdidParser* self = new ( ELeave ) CCea861EdidParser();
    CleanupStack::PushL( self );
    self->ConstructL( aData );
    return self;
    }

// ---------------------------------------------------------------------------
// CCea861EdidParser::~CCea861EdidParser
// ---------------------------------------------------------------------------
//
CCea861EdidParser::~CCea861EdidParser()
    {
    FUNC_LOG;

    delete iParsedInfo;
    }

// ---------------------------------------------------------------------------
// CCea861EdidParser::ParseExtensionBlockL
// ---------------------------------------------------------------------------
//
EXPORT_C TInt CCea861EdidParser::ParseExtensionBlockL( const TExtDataBlock& aData )
    {
    FUNC_LOG;

    TInt err( KErrNone );
    TInt index( 0 );

    // Check first byte to see if tag is 0x2.
    // If not return error.
    if( aData[index] != KBit1 )
        {
        return KErrNotSupported;
        }

    iParsedInfo->iExtensionTag = aData[index];
    index++;

    // Check revision
    iParsedInfo->iRevision = aData[index];
    index++;
    switch( iParsedInfo->iRevision )
        {
        case 1:
            err = ReadCeaVersion1L( aData, index );
            break;
        case 2:
            err = ReadCeaVersion2L( aData, index );
            break;
        case 3:
            err = ReadCeaVersion3L( aData, index );
            break;
        default:
            err = KErrNotSupported;
            break;
        }

    if( err == KErrNone )
        {
        iParsed = ETrue;
        }

    return err;
    }

// ---------------------------------------------------------------------------
// CCea861EdidParser::isExtensionBlockParsed
// ---------------------------------------------------------------------------
//
EXPORT_C TBool CCea861EdidParser::IsExtensionBlockParsed()
    {
    FUNC_LOG;

    return iParsed;
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::GetVIC
// ----------------------------------------------------------------------------
//
EXPORT_C TInt8 CCea861EdidParser::GetVIC( TUint8 aNumber )
    {
    FUNC_LOG;

    if( !iParsed )
        {
        return KErrNotFound;
        }

    if( IsVideoDataBlockSupported() )
        {
        return KErrNotFound;
        }

    if( iParsedInfo->iShortVideoDescriptors )
        {
        TCEA861VideoDataBlock* cur = iParsedInfo->iShortVideoDescriptors;
        TUint8 count = 0;
        while( cur )
            {
            if( cur->iNative )
                {
                if( count == aNumber )
                    {
                    return cur->iVIC;
                    }
                count++;
                }
            cur = cur->iNext;
            }
        }

    return KErrNotFound;
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::Underscan
//
// ----------------------------------------------------------------------------
//
EXPORT_C TBool CCea861EdidParser::Underscan()
    {
    FUNC_LOG;

    if( iParsedInfo->UnderscanDataSupported() )
        {
        return iParsedInfo->iUnderscan;
        }
    return EFalse;
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::BasicAudio
//
// ----------------------------------------------------------------------------
//
EXPORT_C TBool CCea861EdidParser::BasicAudio()
    {
    FUNC_LOG;

    if( iParsedInfo->AudioDataSupported() )
        {
        return iParsedInfo->iAudio;
        }
    return EFalse;
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::YCbCr444
//
// ----------------------------------------------------------------------------
//
EXPORT_C TBool CCea861EdidParser::YCbCr444()
    {
    FUNC_LOG;

    if( iParsedInfo->YCbCr444DataSupported() )
        {
        return iParsedInfo->iYCbCr444;
        }
    return EFalse;
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::YCbCr422
//
// ----------------------------------------------------------------------------
//
EXPORT_C TBool CCea861EdidParser::YCbCr422()
    {
    FUNC_LOG;

    if( iParsedInfo->YCbCr422DataSupported() )
        {
        return iParsedInfo->iYCbCr422;
        }
    return EFalse;
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::TotalNumberOfNativeFormats
//
// ----------------------------------------------------------------------------
//
EXPORT_C TUint8 CCea861EdidParser::TotalNumberOfNativeFormats()
    {
    FUNC_LOG;
    
    return iParsedInfo->TotalNumberOfNativeFormats();
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::IsAudioDataBlockSupported
//
// ----------------------------------------------------------------------------
//
EXPORT_C TBool CCea861EdidParser::IsAudioDataBlockSupported()
    {
    FUNC_LOG;

    return iAudioDataBlockSupported;
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::IsVideoDataBlockSupported
//
// ----------------------------------------------------------------------------
//
EXPORT_C TBool CCea861EdidParser::IsVideoDataBlockSupported()
    {
    FUNC_LOG;

    return iVideoDataBlockSupported;
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::IsVideoDataBlockSupported
//
// ----------------------------------------------------------------------------
//
EXPORT_C TBool CCea861EdidParser::IsVendorSpecificDataBlockSupported()
    {
    FUNC_LOG;

    return iVendorSpecificDataBlockSupported;
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::GetParsedInformation
//
// ----------------------------------------------------------------------------
//
EXPORT_C CCea861ExtEdidInformation* CCea861EdidParser::GetParsedInformation()
    {
    FUNC_LOG;

    return iParsedInfo;
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::IsSpeakerAllocationDataBlockSupported
//
// ----------------------------------------------------------------------------
//
EXPORT_C TBool CCea861EdidParser::IsSpeakerAllocationDataBlockSupported()
    {
    FUNC_LOG;

    return iSpeakerAllocationDataBlockSupported;
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::GetVideoLatency
//
// ----------------------------------------------------------------------------
//
EXPORT_C TUint8 CCea861EdidParser::GetVideoLatency()
    {
    FUNC_LOG;

    // Video latency is kept in Vendor Specific Data Block according to
    // HDMISpecification13a.pdf, table 8-6
    TUint8 latency = 0;
    TCEA861VendorSpecificDataBlock* data = iParsedInfo->iVendorSpecificData;
    if( ( !data ) || ( !IsVendorSpecificDataBlockSupported() ) )
        {
        return latency;
        }

    TCEA861VendorSpecificDataBlockPayload* cur = data->iVendorSpecificPayloadStart;

    // According to HDMI spec table 8-6,  index of video latency is 9
    // Start counting from index 4 
    for( TInt ii = 0; ii < 6; ii++ )
        {
        if( !cur )
            {
            break;
            }
        
        // Byte 8
        else if( ii == 4 )
            {
            // Check if latency field bits are present
            if( !LatencyFieldsPresent( cur->iData ) )
                {
                // Latency fields are not present
                break;
                }
            }

        // Byte 9
        else if( ii == 5 )
            {
            // Get Video_latency byte
            latency = cur->iData;
            }

        cur = cur->iNext;
        }
    
    // Convert latency in to ms
    latency = LatencyInMs( latency );

    return latency;
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::GetAudioLatency
//
// ----------------------------------------------------------------------------
//
EXPORT_C TUint8 CCea861EdidParser::GetAudioLatency()
    {
    FUNC_LOG;

    // Audio latency is kept in Vendor Specific Data Block according to
    // HDMISpecification13a.pdf, table 8-6
    TUint8 latency = 0;
    TCEA861VendorSpecificDataBlock* data = iParsedInfo->iVendorSpecificData;
    if( ( !data ) || ( !IsVendorSpecificDataBlockSupported() ) )
        {
        return latency;
        }

    TCEA861VendorSpecificDataBlockPayload* cur = data->iVendorSpecificPayloadStart;

    // According to HDMI spec table 8-6,  index of audio latency is 10
    // Start counting from index 4 
    for( TInt ii = 0; ii < 7; ii++ )
        {
        if( !cur )
            {
            break;
            }

        // Byte 8
        else if( ii == 4 )
            {
            // Check if latency field bits are present
            if( !LatencyFieldsPresent( cur->iData ) )
                {
                // Latency fields are not present
                break;
                }
            }
        
        // Byte 10
        else if( ii == 6 )
            {
            latency = cur->iData;
            }

        cur = cur->iNext;
        }

    // Convert latency in to ms
    latency = LatencyInMs( latency );

    return latency;
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::GetInterlacedVideoLatency
//
// ----------------------------------------------------------------------------
//
EXPORT_C TUint8 CCea861EdidParser::GetInterlacedVideoLatency()
    {
    // Video latency is kept in Vendor Specific Data Block according to
    // HDMISpecification13a.pdf, table 8-6
    TUint8 latency = 0;
    TCEA861VendorSpecificDataBlock* data = iParsedInfo->iVendorSpecificData;
    if( ( !data ) || ( !IsVendorSpecificDataBlockSupported() ) )
        {
        return latency;
        }

    TCEA861VendorSpecificDataBlockPayload* cur = data->iVendorSpecificPayloadStart;

    // According to HDMI spec table 8-6,  index of video latency is 9 or 11
    // Start counting from index 4
    
    // Start index == 4, video latency == 9 (4 + 5)
    TInt videoLatencyByte = 5;
    for( TInt ii = 0; ii < 8; ii++ )
        {
        if( !cur )
            {
            break;
            }
        
        // Byte 8
        else if( ii == 4 )
            {
            // Check if latency field bits are present
            TUint8 byte8 = cur->iData;
            if( InterlacedLatencyFieldsPresent( byte8 ) )
                {
                // Interlaced latency fields are present
                // Start index == 4, interlaced video latency == 11 (4 + 7)
                videoLatencyByte = 7;
                }
            else if( !LatencyFieldsPresent( byte8 ) )
                {
                // Latency fields are not present
                break;
                }
            }

        // Byte 9 or Byte 11
        else if( ii == videoLatencyByte )
            {
            // Get Video_latency byte
            latency = cur->iData;
            }

        cur = cur->iNext;
        }

    // Convert latency in to ms
    latency = LatencyInMs( latency );

    return latency;
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::GetInterlacedAudioLatency
//
// ----------------------------------------------------------------------------
//
EXPORT_C TUint8 CCea861EdidParser::GetInterlacedAudioLatency()
    {
    FUNC_LOG;

    // Audio latency is kept in Vendor Specific Data Block according to
    // HDMISpecification13a.pdf, table 8-6
    TUint8 latency = 0;
    TCEA861VendorSpecificDataBlock* data = iParsedInfo->iVendorSpecificData;
    if( ( !data ) || ( !IsVendorSpecificDataBlockSupported() ) )
        {
        return latency;
        }

    TCEA861VendorSpecificDataBlockPayload* cur = data->iVendorSpecificPayloadStart;

    // According to HDMI spec table 8-6,  index of audio latency is 10 or 12
    // Start counting from index 4
    
    // Start index == 4, audio latency == 10 (4 + 6)
    TInt audioLatencyByte = 6;
    for( TInt ii = 0; ii < 9; ii++ )
        {
        if( !cur )
            {
            break;
            }

        // Byte 8
        else if( ii == 4 )
            {
            // Check if interlaced latency field bits are present
            TUint8 byte8 = cur->iData;
            if( InterlacedLatencyFieldsPresent( byte8 ) )
                {
                // Start index == 4, interlaced audio latency == 12 (4 + 8)
                audioLatencyByte = 8;
                }
            
            // Check if latency field bits are present
            else if( !LatencyFieldsPresent( byte8 ) )
                {
                // Latency fields are not present
                break;
                }
            }
        
        // Byte 10 or Byte 12
        else if( ii == audioLatencyByte )
            {
            latency = cur->iData;
            }

        cur = cur->iNext;
        }

    // Convert latency in to ms
    latency = LatencyInMs( latency );

    return latency;
    }

// ---------------------------------------------------------------------------
// CCea861EdidParser::ReadCeaVersion1L
// ---------------------------------------------------------------------------
//
TInt CCea861EdidParser::ReadCeaVersion1L( const TExtDataBlock& aData,
    TInt aIndex )
    {
    FUNC_LOG;

    if( aIndex > KEdidParserSizeOfEdidBlock )
        {
        return KErrOverflow;
        }

    for( TInt index = aIndex; index < KEdidParserSizeOfEdidBlock; index++ )
        {
        // Offset. If this is a newer version of CEA-861,
        // then this is skipped (we are already past the offset)
        if( index == 2 )
            {
            iParsedInfo->iOffset = aData[index];
            // Skip over the offset
            while( index < iParsedInfo->iOffset )
                {
                index++;
                }
            }

        if( aData[index] != KEdidPaddingByte ) // padding = 0x00
            {
            // start of 18-byte descriptors: See section 3.10.2 of VESA E-EDID Standard [10]

            TBool first = ETrue;
            if( iParsedInfo->iDescriptorBlocks == 0 )
                {
                iParsedInfo->iDescriptorBlocks
                    = new ( ELeave ) TCEA861TEdidDescriptorBlockList();
                }

            // read the descriptors here
            TEdidDescriptorBlock tmp = GetDescriptorBlock( aData, index );
            if( first )
                {
                first = EFalse;
                iParsedInfo->iDescriptorBlocks->iData = tmp;
                continue;
                }
            TCEA861TEdidDescriptorBlockList* last =
                iParsedInfo->iDescriptorBlocks;
            while( last->iNext != 0 ) // go to last block
                {
                last = last->iNext;
                }
            last->iNext = new ( ELeave ) TCEA861TEdidDescriptorBlockList();
            last = last->iNext;
            last->iData = tmp;
            }
        else
            {
            // this is only padding, no need to do anything
            }

        // after the descriptors there is padding to fill Extension block to 128 bytes.
        // beginning of padding               | End of padding  | Checksum
        // d+(18*n) ..........................| 126             | 127
        if( index == 0x7F )
            {
            iParsedInfo->iChecksum = aData[index];
            // Finished reading
            return KErrNone;
            }
        }

    return KErrNone;
    }

// ---------------------------------------------------------------------------
// CCea861EdidParser::ReadCeaVersion2L
// ---------------------------------------------------------------------------
//
TInt CCea861EdidParser::ReadCeaVersion2L( const TExtDataBlock& aData,
    TInt aIndex )
    {
    FUNC_LOG;

    ReadCeaVersion2and3Common( aData, aIndex );

    return ReadCeaVersion1L( aData, aIndex );
    }

// ---------------------------------------------------------------------------
// CCea861EdidParser::ReadCeaVersion3L
// ---------------------------------------------------------------------------
//
TInt CCea861EdidParser::ReadCeaVersion3L( const TExtDataBlock& aData,
    TInt aIndex )
    {
    FUNC_LOG;

    // first read the common information in versions 2 and 3
    ReadCeaVersion2and3Common( aData, aIndex );

    if( aIndex == 4 ) // start of the Data block collection (see table 39)
        {
        while( aIndex < iParsedInfo->iOffset ) // it is possible to have more than one DataBlockCollection
            {
            TInt ret = ReadCeaDataBlockCollectionL( aData, aIndex );
            if( ret != KErrNone )
                {
                return ret;
                }
            if( aIndex > KEdidParserSizeOfEdidBlock )
                {
                return KErrOverflow;
                }
            }
        // after reading the CEADataBlockCollections, we can read all the descriptors
        }
    else
        {
        // error
        if( aIndex < 4 )
            {
            return KErrNotFound;
            }
        else
            {
            return KErrOverflow;
            }
        }
    return ReadCeaVersion1L( aData, aIndex );
    }

// ---------------------------------------------------------------------------
// CCea861EdidParser::ReadCeaVersion2L
// ---------------------------------------------------------------------------
//
TInt CCea861EdidParser::ReadCeaVersion2and3Common( const TExtDataBlock& aData,
    TInt& aIndex )
    {
    FUNC_LOG;

    for( TInt i = aIndex; i < KEdidParserSizeOfEdidBlock; i++ )
        {
        if( aIndex == 2 ) // offset
            {
            iParsedInfo->iOffset = aData[i];
            // don't skip over the offset
            }
        if( aIndex == 3 )
            {
            // bit 7 = underscan: 1 if sink underscans IT video formats by default
            // bit 6 = audio    : 1 if sink supports basic audio
            // bit 5 = YCbCr 4:4:4 = 1 if sink supports YCbCr 4:4:4 in addition to RGB
            // bit 4 = YCbCr 4:2:2 = 1 if sink supports YCbCr 4:2:2 in addition to RGB
            // lower 4 bits = total number of native DTDs (detailed timing descriptors) (see section 2.2 for definition of "native format")
            iParsedInfo->iUnderscan = ( KBit7 & aData[i] ? ETrue : EFalse );
            iParsedInfo->iAudio = ( KBit6 & aData[i] ? ETrue : EFalse );
            iParsedInfo->iYCbCr444 = ( KBit5 & aData[i] ? ETrue : EFalse );
            iParsedInfo->iYCbCr422 = ( KBit4 & aData[i] ? ETrue : EFalse );
            iParsedInfo->iTotalNumberOfNativeFormats = 0x0F & aData[i];
            aIndex++;
            return KErrNone; // end of common data
            }
        aIndex++;
        }
    return KErrNone;
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::GetDescriptorBlock
//
// ----------------------------------------------------------------------------
//
TEdidDescriptorBlock CCea861EdidParser::GetDescriptorBlock( const TExtDataBlock& aData,
    TInt& aIndex ) const
    {
    FUNC_LOG;

    TEdidDescriptorBlock descBlock;

    descBlock.iPixelClock = GetPixelClock( aData, aIndex );

    descBlock.iHorizontalAddressableVideoPixels
        = GetHorizontalAddressableVideoPixels( aData, aIndex );

    descBlock.iHorizontalBlanking = GetHorizontalBlanking( aData, aIndex );

    descBlock.iVerticalAddressableVideoPixels
        = GetVerticalAddressableVideoPixels( aData, aIndex );

    descBlock.iVerticalBlanking = GetVerticalBlanking( aData, aIndex );

    descBlock.iHorizontalFrontPorch = GetHorizontalFrontPorch( aData, aIndex );

    descBlock.iHorizontalSyncPulse
        = GetHorizontalSyncPulseWidth( aData, aIndex );

    descBlock.iVerticalFrontPorch = GetVerticalFrontPorch( aData, aIndex );

    descBlock.iVerticalSyncPulse = GetVerticalSyncPulseWidth( aData, aIndex );

    descBlock.iHorizontalAddressableImageSize
        = GetHorizontalAddressableImageSizeInMm( aData, aIndex );

    descBlock.iVerticalAddressableImageSize
        = GetVerticalAddressableImageSizeInMm( aData, aIndex );

    descBlock.iHorizontalBorder = GetHorizontalBorderSize( aData, aIndex );

    descBlock.iVerticalBorder = GetVerticalBorderSize( aData, aIndex );

    descBlock.iInterlacedVideo = GetVideoIsInterlaced( aData, aIndex );

    descBlock.iStereoSupport = GetStereoViewingSupport( aData, aIndex );

    descBlock.iSyncs = GetSyncSignalDefinitions( aData, aIndex );

    return descBlock;
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::GetPixelClock
//
// ----------------------------------------------------------------------------
//
TUint16 CCea861EdidParser::GetPixelClock( const TExtDataBlock& aData,
    TInt& aIndex ) const
    {
    FUNC_LOG;

    // Stored Value = Pixel clock ÷ 10,000
    // LSB stored in byte 0 and MSB stored in byte 1
    // 0, 1 2 Range: 10 kHz to 655.35 MHz in 10 kHz steps
    // (00 00)h Reserved: Do not use for Detailed Timing Descriptor
    TUint16 word1 = aData[aIndex];
    TUint16 word2 = aData[aIndex + 1];

    TUint16 pixelClock = word2; // xxxx xxxx 1010 1010
    pixelClock = pixelClock << 8; // 1010 1010 xxxx xxxx shifted to left (MSB)
    pixelClock += word1; // 1010 1010 0101 0101 add the LSB

    aIndex += 2;

    return pixelClock;
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::GetHorizontalAddressableVideoPixels
//
// ----------------------------------------------------------------------------
//
TUint16 CCea861EdidParser::GetHorizontalAddressableVideoPixels( const TExtDataBlock& aData,
    TInt& aIndex ) const
    {
    FUNC_LOG;

    // aIndex at byte #2
    TUint16 word1 = aData[aIndex];
    TUint16 word2 = aData[aIndex + 2];

    // 12bits long, defined in 8 bits of byte 2 and upper nibble of byte 4 (4bits), range 0-4095
    TUint16 horAddrVideo = 0;

    // nullify the 4 lsb
    // ---- ---- xxxx yyyy >> 4  = ---- ---- ---- xxxx
    horAddrVideo = word1 >> 4;
    // return back to original position (4)+4
    // ---- ---- ---- xxxx << 4  = ---- xxxx 0000 0000
    horAddrVideo = horAddrVideo << ( 4 + 4 );
    // ---- xxxx zzzz zzzz = correct values for the 12-bit-long var
    horAddrVideo += word2;

    aIndex++; // to byte #3

    return horAddrVideo;
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::GetHorizontalBlanking
//
// ----------------------------------------------------------------------------
//
TUint16 CCea861EdidParser::GetHorizontalBlanking( const TExtDataBlock& aData,
    TInt& aIndex ) const
    {
    FUNC_LOG;

    // aIndex at byte #3
    TUint16 word1 = aData[aIndex];
    TUint16 word2 = aData[aIndex + 1];

    TUint16 horBlanking = 0;

    // nullify the 4 msb
    // ---- ---- xxxx yyyy << 12 = yyyy ---- ---- ----
    horBlanking = word1 << 12;
    // nullify the 4 msb
    // yyyy ---- ---- ---- >> 4  = ---- yyyy ---- ----
    horBlanking = horBlanking >> 4;
    // ---- yyyy zzzz zzzz = correct values for the 12-bit-long var
    horBlanking += word2;

    aIndex += 2; // to byte #5

    return horBlanking;
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::GetVerticalAddressableVideoPixels
// Vertical Addressable Video in lines
//
// ----------------------------------------------------------------------------
//
TUint16 CCea861EdidParser::GetVerticalAddressableVideoPixels( const TExtDataBlock& aData,
    TInt& aIndex ) const
    {
    FUNC_LOG;

    // aIndex at byte #5
    TUint16 word1 = aData[aIndex + 2];
    TUint16 word2 = aData[aIndex];

    //Upper nibble of byte 7 and the 8 bits of byte 5) - Range is 0 lines to 4095 lines
    TUint16 vertAddrVideo = 0;
    vertAddrVideo = word1 >> 4;
    vertAddrVideo = vertAddrVideo << ( 4 + 4 );
    vertAddrVideo += word2; // like before with HorAddrVideo

    aIndex++; // to byte #6
    return vertAddrVideo;
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::GetVerticalBlanking
//
// ----------------------------------------------------------------------------
//
TUint16 CCea861EdidParser::GetVerticalBlanking( const TExtDataBlock& aData,
    TInt& aIndex ) const
    {
    FUNC_LOG;

    // aIndex at byte #6
    TUint16 word1 = aData[aIndex + 1];
    TUint16 word2 = aData[aIndex];

    // 12bits long, defined in 8 bits of byte 2 and upper nibble of byte 4 (4bits), range 0-4095
    TUint16 vertBlanking = 0;

    // nullify the 4 msb
    // ---- ---- xxxx yyyy << 12 = yyyy ---- ---- ----
    vertBlanking = word1 << 12;
    // nullify the 4 msb
    // yyyy ---- ---- ---- >> 4  = 0000 yyyy ---- ----
    vertBlanking = vertBlanking >> 4;
    // ---- yyyy zzzz zzzz = correct values for the 12-bit-long var
    vertBlanking += word2;

    aIndex += 2; // to byte #8
    return vertBlanking;
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::GetHorizontalFrontPorch
//
// ----------------------------------------------------------------------------
//
TUint16 CCea861EdidParser::GetHorizontalFrontPorch( const TExtDataBlock& aData,
    TInt& aIndex ) const
    {
    FUNC_LOG;

    // aIndex at byte #8

    TUint16 word1 = aData[aIndex];
    TUint16 word2 = aData[aIndex + 3]; // 2 bits from #11

    // Horizontal Front Porch in Pixels (from blanking start to start of sync) is represented by a 10 bit
    // number (Bits 7 & 6 of byte 11 and the 8 bits of byte 8) - Range is 0 pixels to 1023 pixels.

    TUint16 horFrontPorch = 0;

    TUint8 bit67 = word1;
    bit67 = ( bit67 & KBit7 ) | ( bit67 & KBit6 ); // bits 6 and 7 remain

    horFrontPorch = bit67 << 2;
    horFrontPorch += word2;

    aIndex++; // to byte #9
    return horFrontPorch;
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::GetHorizontalSyncPulseWidth
//
// ----------------------------------------------------------------------------
//
TUint16 CCea861EdidParser::GetHorizontalSyncPulseWidth( const TExtDataBlock& aData,
    TInt& aIndex ) const
    {
    FUNC_LOG;

    // aIndex at byte #9
    TUint16 word1 = aData[aIndex];
    TUint16 word2 = aData[aIndex + 2]; // 2 bits from #11

    //Horizontal Sync Pulse Width in Pixels (from the end of the front porch to the start of the back
    //porch) is represented by a 10 bit number (Bits 5 & 4 of byte 11 and the 8 bits of byte 9) - Range
    //is 0 pixels to 1023 pixels.

    TUint16 horSyncPulseWidth = word1;
    horSyncPulseWidth = ( horSyncPulseWidth & KBit5 ) | ( horSyncPulseWidth
        & KBit4 ); // only bits 5 and 4 remain
    horSyncPulseWidth = horSyncPulseWidth << 4;
    horSyncPulseWidth += word2;

    aIndex++; // aIndex at byte #10

    return horSyncPulseWidth;
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::GetVerticalFrontPorch
//
// ----------------------------------------------------------------------------
//
TUint16 CCea861EdidParser::GetVerticalFrontPorch( const TExtDataBlock& aData,
    TInt& aIndex ) const
    {
    FUNC_LOG;

    // Vertical Front Porch in Lines (from blanking start to start of sync) is represented by a 6 bit
    // number (Bits 3 & 2 of byte 11 and the upper nibble of byte 10) - Range is 0 lines to 63 lines.

    // aIndex at byte #10
    TUint16 word1 = aData[aIndex + 1];
    TUint16 word2 = aData[aIndex]; // 2 bits from #11

    TUint16 vertFrontPorch = 0;

    TUint8 bit23 = word1;
    // bits 2 and 3 remain  0000 xx00
    bit23 = ( bit23 & KBit3 ) | ( bit23 & KBit2 );

    // 00xx 0000
    vertFrontPorch = bit23 << 2;
    // 00xx yyyy
    vertFrontPorch += ( word2 >> 4 );

    //Index remains at byte #10

    return vertFrontPorch;
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::GetVerticalSyncPulseWidth
//
// ----------------------------------------------------------------------------
//
TUint8 CCea861EdidParser::GetVerticalSyncPulseWidth( const TExtDataBlock& aData,
    TInt& aIndex ) const
    {
    FUNC_LOG;

    // Vertical Sync Pulse Width in Lines (from the end of the front porch to the start of the back
    // porch) is represented by a 6 bit number (Bits 1 & 0 of byte 11 and the lower nibble of byte 10 -
    // Range is 0 lines to 63 lines.

    // aIndex at byte #10
    TUint16 word1 = aData[aIndex];
    TUint16 word2 = aData[aIndex + 1]; // 2 bits from #11

    // Vertical Sync Pulse Width in Lines (from the end of the front porch to the start of the back
    // porch) is represented by a 6 bit number (Bits 1 & 0 of byte 11 and the lower nibble of byte 10 -
    // Range is 0 lines to 63 lines.
    TUint8 vertSyncPulse = 0;

    // bits 1 and 0 remain   0000 00xx
    word2 = ( word2 & KBit1 ) | ( word2 & KBit0 );

    // 00xx 0000
    vertSyncPulse = word2 << 4;
    // only the lower nibble, %16 removes the upper nibble
    word1 = word1 % 16;
    // 00xx yyyy
    vertSyncPulse += word1;

    aIndex += 2; // to byte #12

    return vertSyncPulse;
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::GetHorizontalAddressableImageSizeInMm
// Horizontal Addressable Video Image Size in mm
//
// ----------------------------------------------------------------------------
//
TUint16 CCea861EdidParser::GetHorizontalAddressableImageSizeInMm( const TExtDataBlock& aData,
    TInt& aIndex ) const
    {
    FUNC_LOG;

    // aIndex at byte #12
    TUint16 word1 = aData[aIndex + 2];
    TUint16 word2 = aData[aIndex];

    //Horizontal Addressable Video Image Size in mm is represented by a 12 bit number (Upper
    //nibble of byte 14 and the 8 bits of byte 12) - Range is 0 mm to 4095 mm.

    // first nullify 4 lsb
    TUint16 hais = word1 >> 4;
    // and then move back the first 4 and then again 4
    hais = hais << ( 4 + 4 );
    hais += word2;

    aIndex++; // to byte #13

    return hais;
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::GetVerticalAddressableImageSizeInMm
//
// ----------------------------------------------------------------------------
//
TUint16 CCea861EdidParser::GetVerticalAddressableImageSizeInMm( const TExtDataBlock& aData,
    TInt& aIndex ) const
    {
    FUNC_LOG;

    // aIndex at byte #13
    TUint16 word1 = aData[aIndex + 1];
    TUint16 word2 = aData[aIndex];

    //Vertical Addressable Video Image Size in mm is represented by a 12 bit number (Lower nibble
    //of byte 14 and the 8 bits of byte 13) - Range is 0 mm to 4095 mm.

    TUint16 vais = word1;
    // first nullify 4 msb and then move back 1 nibble
    vais = vais << 12;
    vais = vais >> 4;
    vais += word2;

    aIndex += 2; // to byte #15

    return vais;
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::GetHorizontalBorderSize
//
// ----------------------------------------------------------------------------
//
TUint8 CCea861EdidParser::GetHorizontalBorderSize( const TExtDataBlock& aData,
    TInt& aIndex ) const
    {
    FUNC_LOG;

    // aIndex at byte #15
    TUint16 word1 = aData[aIndex];

    //Right Horizontal Border or Left Horizontal Border in Pixels is represented by an 8 bit number
    //(the 8 bits of byte 15) - Range is 0 pixels to 255 pixels.

    // the same value is used for (left and right) / (top and bottom)
    aIndex++; // to byte #16
    return word1;
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::GetVerticalBorderSize
// Top Vertical Border or Bottom Vertical Border in Lines
//
// ----------------------------------------------------------------------------
//
TUint8 CCea861EdidParser::GetVerticalBorderSize( const TExtDataBlock& aData,
    TInt& aIndex ) const
    {
    FUNC_LOG;

    // aIndex at byte #16
    TUint16 word1 = aData[aIndex];

    //Top Vertical Border or Bottom Vertical Border in Lines is represented by an 8 bit number (the
    //8 bits of byte 16) - Range is 0 lines to 255 lines.

    aIndex++; // to byte #17
    return word1;
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::GetVideoIsInterlaced
//
// ----------------------------------------------------------------------------
//
TBool CCea861EdidParser::GetVideoIsInterlaced( const TExtDataBlock& aData,
    TInt& aIndex ) const
    {
    FUNC_LOG;

    // aIndex at byte #17
    TUint16 word1 = aData[aIndex];

    // byte 17
    //Bytes Bit Definitions Detailed Timing Definitions
    //7 6 5 4 3 2 1 0 Signal Interface Type:
    //0 _ _ _ _ _ _ _ Non-Interlaced (1 frame = 1 field)
    //1 _ _ _ _ _ _ _ Interlaced (1 frame = 2 fields)

    // Index remains at byte #17

    return KBit7 & word1;
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::GetStereoViewingSupport
//
// ----------------------------------------------------------------------------
//
TEdidStereoViewingSupport CCea861EdidParser::GetStereoViewingSupport( const TExtDataBlock& aData,
    TInt& aIndex ) const
    {
    FUNC_LOG;

    // aIndex at byte #17
    TUint16 word1 = aData[aIndex];

    // See TEdidStereoViewingSupport enum declaration

    // byte 17
    //Bytes Bit Definitions Detailed Timing Definitions
    //_ 6 5 _ _ _ _ 0 Stereo Viewing Support:
    //  0 0 _ _ _ _ x Normal Display – No Stereo. The value of bit 0 is "don't care"
    //  0 1 _ _ _ _ 0 Field sequential stereo, right image when stereo sync signal = 1
    //  1 0 _ _ _ _ 0 Field sequential stereo, left image when stereo sync signal = 1
    //  0 1 _ _ _ _ 1 2-way interleaved stereo, right image on even lines
    //  1 0 _ _ _ _ 1 2-way interleaved stereo, left image on even lines
    //  1 1 _ _ _ _ 0 4-way interleaved stereo
    //  1 1 _ _ _ _ 1 Side-by-Side interleaved stereo
    TUint8 bit17 = word1;

    // Index remains at byte #17

    if( !( ( KBit6 & bit17 ) | ( KBit5 & bit17 ) ) ) //  0 0 _ _ _ _ x Normal Display – No Stereo.
        { //                The value of bit 0 is "don't care"
        return ENormalDisplay;
        }
    else
        {
        if( !( KBit6 & bit17 ) && ( KBit5 & bit17 ) && !( KBit0 & bit17 ) ) //  0 1 _ _ _ _ 0 Field sequential stereo,
            { //                right image when stereo sync signal=1
            return EFieldSequentialStereoRightWhenStereoSyncSignal1;
            }
        else
            {
            if( ( KBit6 & bit17 ) && !( KBit5 & bit17 ) && !( KBit0 & bit17 ) ) //  1 0 _ _ _ _ 0 Field sequential stereo,
                { //                left image when stereo sync signal=1
                return EFieldSequentialStereoLeftWhenStereoSyncSignal1;
                }
            else
                {
                if( !( KBit6 & bit17 ) && ( KBit5 & bit17 ) && ( KBit0 & bit17 ) ) //  0 1 _ _ _ _ 1 2-way interleaved stereo,
                    { //                right image on even lines
                    return ETwoWayInterleavedStereoRightImageOnEvenLines;
                    }
                else
                    {
                    if( ( KBit6 & bit17 ) && !( KBit5 & bit17 ) && ( KBit0
                        & bit17 ) )//  1 0 _ _ _ _ 1 2-way interleaved stereo,
                        { //                left image on even lines
                        return ETwoWayInterleavedStereoLeftImageOnEvenLines;
                        }
                    else
                        {
                        if( ( KBit6 & bit17 ) && ( KBit5 & bit17 ) && !( KBit0
                            & bit17 ) ) //  1 1 _ _ _ _ 0 4-way interleaved stereo
                            {
                            return EFourWayInterleaverStereo;
                            }
                        else
                            {
                            if( ( KBit6 & bit17 ) && ( KBit5 & bit17 )
                                && ( KBit0 & bit17 ) ) //  1 1 _ _ _ _ 1 Side-by-Side interleaved stereo
                                {
                                return ESideBySideInterleavedStere;
                                }
                            else
                                {
                                return EUnknownStereoViewingSupport;
                                }
                            }
                        }
                    }
                }
            }
        }
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::GetAnalogSyncSignalDefinitions
//
// ----------------------------------------------------------------------------
//
TEdidSyncSignalDefinitions CCea861EdidParser::GetSyncSignalDefinitions( const TExtDataBlock& aData,
    TInt& aIndex ) const
    {
    FUNC_LOG;

    // aIndex at byte #18
    TUint8 byte17 = aData[aIndex];

    // byte 17
    // 4 3 2 1 _ Analog Sync Signal Definitions:
    //------------------------------------------
    // 0 0 _ _ _ Analog Composite Sync:
    // 0 1 _ _ _ Bipolar Analog Composite Sync:

    aIndex++; // advance to byte #18 finally -> this descriptor block is now finished

    if( !( KBit4 & byte17 ) ) // if bit4 == 0 then this is analog display
        {
        return GetAnalogSyncSignalDefinitions( byte17 );
        }

    return GetDigitalSyncSignalDefinitions( byte17 );
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::GetAnalogSyncSignalDefinitions
//
// ----------------------------------------------------------------------------
//
TEdidSyncSignalDefinitions CCea861EdidParser::GetAnalogSyncSignalDefinitions( const TUint8 aByte17 )
    {
    FUNC_LOG;

    // byte 17
    // 4 3 2 1 _ Analog Sync Signal Definitions:
    //------------------------------------------
    // 0 0 _ _ _ Analog Composite Sync:
    // 0 1 _ _ _ Bipolar Analog Composite Sync:
    // 0 _ 0 _ _ ---------- Without Serrations;
    // 0 _ 1 _ _ ---------- With Serrations (H-sync during V-sync);
    // 0 _ _ 0 _ -------------------- Sync On Green Signal only
    // 0 _ _ 1 _ -------------------- Sync On all three (RGB) video signals

    TBool ACS = !( KBit3 & aByte17 ); // if true -> bipolar analog composite sync
    TBool serrations = KBit2 & aByte17;
    TBool syncOnGreen = !( KBit1 & aByte17 );

    if( ACS )
        {
        if( !serrations )
            {
            if( syncOnGreen )
                {
                return EAnalogCompositeSyncWithoutSerrationsSyncSyncOnGreenSignalOnly;
                }
            else
                {
                return EAnalogCompositeSyncWithoutSerrationsSyncOnAllThreeVideoSignals;
                }
            }
        else
            {
            if( syncOnGreen )
                {
                return EAnalogCompositeSyncWithoutSerrationsSyncSyncOnGreenSignalOnly;
                }
            else
                {
                return EAnalogCompositeSyncWithoutSerrationsSyncOnAllThreeVideoSignals;
                }
            }
        }
    else
        {
        if( !serrations )
            {
            if( syncOnGreen )
                {
                return EAnalogBipolarAnalogCompositeSyncWithoutSerrationsSyncSyncOnGreenSignalOnly;
                }
            else
                {
                return EAnalogBipolarAnalogCompositeSyncWithoutSerrationsSyncOnAllThreeVideoSignals;
                }
            }
        else
            {
            if( syncOnGreen )
                {
                return EAnalogBipolarAnalogCompositeSyncWithSerrationsSyncSyncOnGreenSignalOnly;
                }
            else
                {
                return EAnalogBipolarAnalogCompositeSyncWithSerrationsSyncOnAllThreeVideoSignals;
                }
            }
        }
    // unreachable
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::GetDigitalSyncSignalDefinitions
//
// ----------------------------------------------------------------------------
//
TEdidSyncSignalDefinitions CCea861EdidParser::GetDigitalSyncSignalDefinitions( const TUint8 aByte17 )
    {
    FUNC_LOG;

    //        4 3 2 1 0 bit
    //        --------------
    //        1 0 _ _ _ Digital Composite Sync:
    //        1 0 0 _ _ ---------- Without Serrations;
    //        1 0 1 _ _ ---------- With Serrations (H-sync during V-sync);
    //        1 1 _ _ _ Digital Separate Sync:
    //        1 1 0 _ _ ---------- Vertical Sync is Negative;
    //        1 1 1 _ _ ---------- Vertical Sync is Positive;
    //        1 _ _ 0 _ -------------------- Horizontal Sync is Negative (outside of V-sync)
    //        1 _ _ 1 _ -------------------- Horizontal Sync is Positive (outside of V-sync)

    TBool digComp = !( KBit3 & aByte17 ); // ETrue == digital separate sync
    TBool bit2 = KBit2 & aByte17;

    if( digComp ) // digital composite sync
        {
        if( !bit2 ) // without serrations
            {
            return EDigitalCompositeSyncWithoutSerrations;
            }
        else // with serrations
            {
            return EDigitalCompositeSyncWithSerrations;
            }
        } // endif  digital composite sync
    else //        digital separate sync
        {
        TBool bit1 = KBit1 & aByte17;

        if( !bit2 ) // vertical = neg
            {
            if( !bit1 )
                { // vertical = neg, horizontal = neg
                return EDigitalSeparateSyncVerticalSyncIsNegativeHorizontalSyncIsNegative;
                }
            else
                { // vertical = neg, horizontal = pos
                return EDigitalSeparateSyncVerticalSyncIsNegativeHorizontalSyncIsPositive;
                }
            }
        else // vertical = pos
            {
            if( !bit1 )
                { // vertical = pos, horizontal = neg
                return EDigitalSeparateSyncVerticalSyncIsPositiveHorizontalSyncIsNegative;
                }
            else
                { // vertical = pos, horizontal = pos
                return EDigitalSeparateSyncVerticalSyncIsPositiveHorizontalSyncIsPositive;
                }
            }
        }
    // unreachable
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::ReadCeaDataBlockCollectionL
//
// ----------------------------------------------------------------------------
//
TInt CCea861EdidParser::ReadCeaDataBlockCollectionL( const TExtDataBlock& aData,
    TInt& aIndex )
    {
    FUNC_LOG;

    // bits 5-7         | bits 0-4
    // Tag Code         | Length = total number of video bytes following this byte (L1)
    TInt tag = 0; // video tag code
    TInt L1 = 0; // lenght (L1)
    if( KBit7 & aData[aIndex] )
        {
        tag += 4;
        }
    if( KBit6 & aData[aIndex] )
        {
        tag += 2;
        }
    if( KBit5 & aData[aIndex] )
        {
        tag += 1;
        }
    L1 = aData[aIndex] & 0x1F; // clear the 3 MSBits

    aIndex++; // jump over the first byte

    TInt auxTag = 0;
    switch( tag )
        {
        case 0:
            //reserved
            break;
        case 1:
            ReadCea861ShortAudioDataBlockL( aData, aIndex, L1 );
            break;
        case 2:
            ReadCea861ShortVideoDataBlockL( aData, aIndex, L1 );
            break;
        case 3:
            ReadCea861VendorSpecificDataBlockL( aData, aIndex, L1 );
            break;
        case 4:
            ReadCea861SpeakerAllocationDataBlock( aData, aIndex, L1 );
            break;
        case 5:
            //VESA DTC Data Block
            ReadUnknownTagCode( aData, aIndex, L1 );
            break;
        case 6:
            //reserved
            ReadUnknownTagCode( aData, aIndex, L1 );
            break;
        case 7:
            //use extended tag (second byte holds the extended tag-code)
            // (see table 43 in CEA-861 spec) (if using repeater, then these information must be re-transmitted)
            auxTag = aData[aIndex + 1];
            if( auxTag == 5 ) // colorimetry, see table 51, table 52 and table 53 in CEA-861-E.pdf
                {
                }
            else if( auxTag == 0x07 ) // video Capability Data Block, see table 54
                {
                ReadVideoCapabilityDataBlockL( aData, aIndex, L1 );
                }
            else if( auxTag == 0x01 ) // Vendor-Specific Video Data Block, see table 56
                {
                }
            else if( auxTag == 0x11 ) // Vendor-Specific Audio Data Block, see table 57
                {
                }
            // TODO: these should be read as well, not just as unknown
            ReadUnknownTagCode( aData, aIndex, L1 );
            break;
        default:
            // if tag-code is unknown, we still must read through it
            ReadUnknownTagCode( aData, aIndex, L1 );
            break;
        }

    return KErrNone;
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::ReadCea861ShortAudioDataBlockL
//
// ----------------------------------------------------------------------------
//
void CCea861EdidParser::ReadCea861ShortAudioDataBlockL( const TExtDataBlock& aData,
    TInt& aIndex,
    const TInt8 aLen )
    {
    FUNC_LOG;

    iAudioDataBlockSupported = ETrue;

    if( !iParsedInfo->iShortAudioDescriptors ) // linked list
        {
        iParsedInfo->iShortAudioDescriptors
            = new ( ELeave ) TCEA861AudioDataBlock();
        }
    TCEA861AudioDataBlock* cur = iParsedInfo->iShortAudioDescriptors;

    while( cur->iNext != 0 )
        {
        cur = cur->iNext; // jump to the end
        }

    TBool first = ETrue;
    for( int i = 0; i < aLen; i++ )
        {
        // read aLen-amount of short video descriptors

        // first link is a special case
        if( ( iParsedInfo->iShortAudioDescriptors == cur ) && first )
            {
            first = EFalse;
            cur->iByte1 = aData[aIndex];
            cur->iByte2 = aData[aIndex + 1];
            cur->iByte3 = aData[aIndex + 2];
            DetermineAudioBlockInformation( cur );
            aIndex += 3;
            i += 3;
            continue;
            }

        // create new data blocks
        cur->iNext = new ( ELeave ) TCEA861AudioDataBlock();
        cur = cur->iNext;
        cur->iByte1 = aData[aIndex];
        cur->iByte2 = aData[aIndex + 1];
        cur->iByte3 = aData[aIndex + 2];
        cur->iNext = 0;

        DetermineAudioBlockInformation( cur );

        aIndex += 3;
        i += 3;
        }
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::ReadCea861ShortVideoDataBlockL
//
// ----------------------------------------------------------------------------
//
void CCea861EdidParser::ReadCea861ShortVideoDataBlockL( const TExtDataBlock& aData,
    TInt& aIndex,
    const TInt8 aLen )
    {
    FUNC_LOG;

    TBool first = ETrue;
    iVideoDataBlockSupported = ETrue;
    if( !iParsedInfo->iShortVideoDescriptors ) // linked list
        {
        iParsedInfo->iShortVideoDescriptors
            = new ( ELeave ) TCEA861VideoDataBlock();
        iParsedInfo->iShortVideoDescriptors->iNext = 0; // make sure there are no stray pointers
        }

    TCEA861VideoDataBlock* cur = iParsedInfo->iShortVideoDescriptors;
    while( cur->iNext != 0 )
        {
        cur = cur->iNext; // jump to the end
        first = EFalse; // there is already some links, so set first to false
        }

    for( int i = 0; i < aLen; i++ )
        {

        // read aLen-amount of short video descriptors

        // first link is a special case
        if( ( iParsedInfo->iShortVideoDescriptors == cur ) && first )
            {
            first = EFalse;
            cur->iNative = KBit7 & aData[aIndex];
            cur->iVIC = ( ~KBit7 ) & aData[aIndex]; // nullify the most signicant bit
            cur->iNext = 0;
            aIndex++;
            continue;
            }

        // create new data blocks
        cur->iNext = new ( ELeave ) TCEA861VideoDataBlock();
        cur = cur->iNext;
        cur->iNative = KBit7 & aData[aIndex];
        cur->iVIC = ( ~KBit7 ) & aData[aIndex]; // nullify the most signicant bit
        cur->iNext = 0;
        aIndex++;
        }
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::ReadCea861VendorSpecificDataBlockL
//
// ----------------------------------------------------------------------------
//
void CCea861EdidParser::ReadCea861VendorSpecificDataBlockL( const TExtDataBlock& aData,
    TInt& aIndex,
    TInt8 aLen )
    {
    FUNC_LOG;

    iVendorSpecificDataBlockSupported = ETrue;

    if( !iParsedInfo->iVendorSpecificData )
        {
        iParsedInfo->iVendorSpecificData = new ( ELeave ) TCEA861VendorSpecificDataBlock();
        }


    TUint8 data1 = aData[aIndex]; // least significant byte first
    TUint8 data2 = aData[aIndex + 1];
    TUint8 data3 = aData[aIndex + 2];
    aIndex += 3; // three bytes of IEEE registration identifier

    iParsedInfo->iVendorSpecificData->iIEEE24bitRegistrationIdentifier = 0; // convert from LSB -> MSB
    iParsedInfo->iVendorSpecificData->iIEEE24bitRegistrationIdentifier += data3;
    iParsedInfo->iVendorSpecificData->iIEEE24bitRegistrationIdentifier
        = iParsedInfo->iVendorSpecificData->iIEEE24bitRegistrationIdentifier
            << 8;
    iParsedInfo->iVendorSpecificData->iIEEE24bitRegistrationIdentifier += data2;
    iParsedInfo->iVendorSpecificData->iIEEE24bitRegistrationIdentifier
        = iParsedInfo->iVendorSpecificData->iIEEE24bitRegistrationIdentifier
            << 8;
    iParsedInfo->iVendorSpecificData->iIEEE24bitRegistrationIdentifier += data1;

    aLen -= 3; // this is needed: Vendor specific payload length = L4-3bytes

    TBool first = ETrue;
    if( iParsedInfo->iVendorSpecificData->iVendorSpecificPayloadStart == 0 )
        {
        iParsedInfo->iVendorSpecificData->iVendorSpecificPayloadStart
            = new ( ELeave ) TCEA861VendorSpecificDataBlockPayload();
        }
    TCEA861VendorSpecificDataBlockPayload* cur =
        iParsedInfo->iVendorSpecificData->iVendorSpecificPayloadStart;
    // traverse to the end of the list
    while( cur->iNext != 0 )
        {
        cur = cur->iNext;
        }

    for( int i = 0; i < aLen; i++ )
        {
        if( ( cur == iParsedInfo->iVendorSpecificData->iVendorSpecificPayloadStart )
            && first ) // first byte is special case
            {
            first = EFalse;
            cur->iData = aData[aIndex];
            cur->iNext = NULL;
            aIndex++;
            continue;
            }
        cur->iNext = new ( ELeave ) TCEA861VendorSpecificDataBlockPayload();
        cur = cur->iNext;
        cur->iData = aData[aIndex];
        cur->iNext = 0;
        aIndex++;
        }
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::ReadCea861SpeakerAllocationDataBlock
//
// ----------------------------------------------------------------------------
//
void CCea861EdidParser::ReadCea861SpeakerAllocationDataBlock( const TExtDataBlock& aData,
    TInt& aIndex,
    const TInt8 aLen )
    {
    FUNC_LOG;

    // read the 3 bytes
    if( aLen != 3 )
        {
        return;
        }

    iSpeakerAllocationDataBlockSupported = ETrue;

    for( TInt i = 0; i < aLen; i++ )
        {
        switch( i )
            {
            case 0:
                iParsedInfo->iSpeakerAllocationData.iByte1 = aData[aIndex];
                break;
            case 1:
                iParsedInfo->iSpeakerAllocationData.iByte2 = aData[aIndex];
                break;
            case 2:
                iParsedInfo->iSpeakerAllocationData.iByte3 = aData[aIndex];
                break;
            default:
                // error
                break;
            }
        aIndex++;
        }
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::ReadVideoCapabilityDataBlockL
//
// ----------------------------------------------------------------------------
//
void CCea861EdidParser::ReadVideoCapabilityDataBlockL( const TExtDataBlock& aData,
    TInt& aIndex,
    const TInt8 aLen )
    {
    FUNC_LOG;

    aIndex++; // jump to the extended tag code (aLen is the length from extended tag to the end)

    if( iParsedInfo->iVideoCapabilityDataBlock == 0 )
        {
        iParsedInfo->iVideoCapabilityDataBlock
            = new ( ELeave ) TCEA861VideoCapabilityDataBlock();
        }
    TCEA861VideoCapabilityDataBlock* cur =
        iParsedInfo->iVideoCapabilityDataBlock;
    TBool first = ETrue;
    while( cur->iNext != 0 )
        {
        first = EFalse;
        cur = cur->iNext;
        }

    // Payload currently only contains a single byte in addition to the extended tag code. The Source should ignore such additional bytes when present.
    for( int i = 0; i < aLen; i++ )
        {
        if( i == 1 )
            {
            if( !first ) // if this wasn't the first time we are reading to this
                {
                cur->iNext = new ( ELeave ) TCEA861VideoCapabilityDataBlock(); // sets Next to 0
                cur = cur->iNext;
                }
            cur->iInitialized = ETrue;
            cur->iQuantizationRange = aData[aIndex] & KBit7;
            cur->iQuantizationRangeSelectable = aData[aIndex] & KBit6;
            cur->iS_PT1 = aData[aIndex] & KBit5;
            cur->iS_PT0 = aData[aIndex] & KBit4;
            cur->iS_IT1 = aData[aIndex] & KBit3;
            cur->iS_IT0 = aData[aIndex] & KBit2;
            cur->iS_CE1 = aData[aIndex] & KBit1;
            cur->iS_CE0 = aData[aIndex] & KBit0;
            cur->iNext = 0;
            }
        aIndex++;
        }
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::readUnknownTagCode
//
// ----------------------------------------------------------------------------
//
void CCea861EdidParser::ReadUnknownTagCode( const TExtDataBlock& /*aData*/,
    TInt& aIndex,
    const TInt8 aLen )
    {
    FUNC_LOG;

    // Skip the data. This must be done if the tag is unknown
    aIndex += aLen;
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::DetermineAudioBlockInformation
//
// ----------------------------------------------------------------------------
//
void CCea861EdidParser::DetermineAudioBlockInformation( TCEA861AudioDataBlock* aAudioBlock )
    {
    FUNC_LOG;

    // x6543xxx we need the information in the bytes 6543
    aAudioBlock->iAudioFormatCode = ( KBit3 | KBit2 | KBit1 | KBit0 )
        & ( aAudioBlock->iByte1 >> 3 );
    aAudioBlock->iSupport192kHz = ( aAudioBlock->iByte2 & KBit6 ? ETrue : EFalse );
    aAudioBlock->iSupport176kHz = ( aAudioBlock->iByte2 & KBit5 ? ETrue : EFalse );
    aAudioBlock->iSupport96kHz = ( aAudioBlock->iByte2 & KBit4 ? ETrue : EFalse );
    aAudioBlock->iSupport88kHz = ( aAudioBlock->iByte2 & KBit3 ? ETrue: EFalse );
    aAudioBlock->iSupport48kHz = ( aAudioBlock->iByte2 & KBit2 ? ETrue : EFalse );
    aAudioBlock->iSupport44kHz = ( aAudioBlock->iByte2 & KBit1 ? ETrue : EFalse );
    aAudioBlock->iSupport32kHz = ( aAudioBlock->iByte2 & KBit0 ? ETrue : EFalse );
    aAudioBlock->iMaxChannels = aAudioBlock->iByte1 & 0x07; // only the 3 LSB is used
    //TODO: 3rd byte shall be read according to the audioformatcode (see table 45 in CEA-861-E.pdf)
    switch( aAudioBlock->iAudioFormatCode )
        {
        case 1:
            aAudioBlock->iSupport24Bit = ( aAudioBlock->iByte3 & KBit2 ? ETrue : EFalse );
            aAudioBlock->iSupport20Bit = ( aAudioBlock->iByte3 & KBit1 ? ETrue : EFalse );
            aAudioBlock->iSupport16Bit = ( aAudioBlock->iByte3 & KBit0 ? ETrue : EFalse );
            break;
        case 2: //cases 2-8
        case 3:
        case 4:
        case 5:
        case 6:
        case 7:
        case 8:
            aAudioBlock->iMaxBitrate = aAudioBlock->iByte3 * 8;
            break;
        case 9: // cases 9-13
        case 10:
        case 11:
        case 12:
        case 13:
            // byte3 is Audio format code dependent value
            break;
        case 14:
            // byte3 bits 0,1,2 is profile. bits 3-7 are reserved.
            break;
        case 15: // bits 7-3 = audio format code extension, bits 2-0
            aAudioBlock->iAudioFormatCodeExtension = aAudioBlock->iByte3 >> 3;
            break;
        default:
            // error
            break;
        }
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::LatencyFieldsPresent
//
// ----------------------------------------------------------------------------
//
TBool CCea861EdidParser::LatencyFieldsPresent( TUint8 aByte ) const
    {
    FUNC_LOG;
    
    TBool present = EFalse;
    
    // Latency_Fields_Preset bit is bit6 (01000000) in the aByte
    if( aByte & 0x40 )
        {
        present = ETrue;
        }
    return present;
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::InterlacedLatencyFieldsPresent
//
// ----------------------------------------------------------------------------
//
TBool CCea861EdidParser::InterlacedLatencyFieldsPresent( TUint8 aByte ) const
    {
    FUNC_LOG;

    TBool present = EFalse;
    
    // Latency_Fields_Preset bit is bit7 (10000000) in the aByte
    if( aByte & 0x80 )
        {
        present = ETrue;
        }
    return present;
    }

// ----------------------------------------------------------------------------
// CCea861EdidParser::InterlacedLatencyFieldsPresent
//
// ----------------------------------------------------------------------------
//
TUint8 CCea861EdidParser::LatencyInMs( TUint8 aByte ) const
    {
    FUNC_LOG;
    
    TUint8 latency = 0;
    if( aByte > 0 && aByte <= 251 )
        {
        latency = ( aByte - 1 ) * 2;
        }
    
    return latency;
    }

// End of file
