blob: c0e5eaa1e822c672409bc46089d13bff025e4435 [file] [log] [blame]
#!/usr/bin/python
# Copyright 2014 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
####################################################
# EDID Parser
# Takes in binary blob EDID
# This parser is primarily based on the following:
# 1. "VESA Enhanced Extended Display Identification Data Standard", Release A,
# Revision 2, Sept. 25, 2006.
# 2. "A DTV Profile for Uncompressed High Speed Digital Interfaces",
# ANSI/CEA-861-F, Aug., 2013.
# 3. HDMI spec.
####################################################
"""Parses EDID and establishes command line options for EDID analysis."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import argparse
import re
import edid.data_block as data_block
import edid.descriptor as descriptor
import edid.edid as edid
import edid.extensions as extensions
import edid.tools as tools
import edid.video_block as video_block
from six.moves import map
from six.moves import range
LAYOUT_MODE = 0
NORMAL_MODE = 1
VERBOSE_MODE = 2
TYPE_ALL = 'all'
TYPE_BASE = 'base'
TYPE_VENDOR = 'vendor'
TYPE_BD = 'bd'
TYPE_DCC = 'dcc'
TYPE_ET = 'et'
TYPE_ST = 'st'
TYPE_DP = 'dp'
TYPE_XALL = 'xall'
RAW_OFF = 0
RAW_HEX = 1
RAW_DEC = 2
VALID_TYPES = set([TYPE_ALL, TYPE_BASE, TYPE_VENDOR, TYPE_BD, TYPE_DCC, TYPE_ET,
TYPE_ST, TYPE_DP, TYPE_XALL])
VALID_TYPE_MESSAGE = ('Type options include: all (all info), base (base block),'
' vendor (vendor and product info, bd (basic display in '
'base block), dcc (display x, y chromaticity coordinates '
'in base block), et (established timings in base block), '
'st (standard timings in base block), dp (descriptors in '
'base block), x<n> (show nth extension), x-all (show all '
'extensions). Default: \'all\'.')
type_help_string = ('Types of information to print, listed as a single '
'string (command separated). %s' % VALID_TYPE_MESSAGE)
################
# FUNCTIONS #
###################
def BytesFromFile(filename):
"""Reads the EDID from binary blob form into list form.
Args:
filename: The name of the binary blob.
Returns:
The list of bytes that make up the EDID.
"""
with open(filename, 'rb') as f:
chunk = f.read()
return list(map(ord, chunk))
def PrintSpace(num=1):
"""Formats print out nicely by adding space between sections.
Args:
num: Denotes a smaller space (num = 0) or regular 3-line space (num = 1).
"""
if num == 1 or num == 2:
print('\n\n\n')
elif num == 0:
print('\n')
def PrintList(alist, mode, print_format):
"""Prints out a list of properties and their values in specified format.
Args:
alist: The list of tuples to print.
mode: The level of verbosity for analysis.
print_format: The string format for printing.
"""
for x, s in alist:
if mode == NORMAL_MODE and not s:
continue
print(print_format % (x, s))
def GetManufacturerInfo(e, mode, raw_mode):
"""Prints and interprets the manufacturer information of an EDID.
Args:
e: The EDID being parsed.
mode: The level of verbosity for analysis.
raw_mode: The type of raw data print out, if any.
"""
print('[Manufacturing/vendor info]')
if raw_mode:
PrintRawRange(e.GetData(), raw_mode, 0x08, 0x12)
if mode == LAYOUT_MODE:
return
info = [
['Manufacturer ID:', e.manufacturer_id],
['ID Product Code:', e.product_code],
['Serial number:', e.serial_number],
['Week of manufacture:', e.manufacturing_week],
['Year of manufacture:', e.manufacturing_year],
['Model year:', e.model_year]
]
PrintList(info, mode, ' %-22s %s')
def GetBasicDisplay(e, mode, raw_mode):
"""Prints and interprets the basic display information of an EDID.
Args:
e: The EDID being parsed.
mode: The level of verbosity for analysis.
raw_mode: The type of raw data print out, if any.
"""
print('[Basic Display Information]')
if raw_mode:
PrintRawRange(e.GetData(), raw_mode, 0x14, 0x19)
if mode == LAYOUT_MODE:
return
bd = e.basic_display
# Video input definition
# 0 = Analog, 1 = Digital
if bd.video_input_type: # Digital
print('Digital Video Signal Interface')
dig_info = [
['Color Bit Depth:', bd.color_bit_depth],
['Digital Video Interface Standard Support:',
bd.digital_supports]
]
PrintList(dig_info, mode, ' %-50s %s')
elif not bd.video_input_type: # Analog
print('Analog Video Signal Interface')
analog_info = [
['Video white and sync levels:', bd.signal_level],
['Blank-to-black setup expected:', bd.blank_black],
['Separate sync supported:', bd.separate_sync],
['Composite sync (on HSync) supported:', bd.composite_sync],
['Sync on green supported:', bd.green_sync],
['VSync serrated when composite/sync-on-green used:',
bd.vsync_pulse]
]
PrintList(analog_info, mode, ' %-50s %s')
# Shared basic display properties (both analog/digital)
if not bd.horizontal_dim or not bd.vertical_dim:
max_dim = None
else:
max_dim = '%d x %d' % (bd.horizontal_dim, bd.vertical_dim)
pix_str = ('Preferred timing includes native timing pixel\n %-50s'
% ' format and refresh rate:')
info = [
['Maximum dimensions (cm):', max_dim],
['Aspect ratio (portrait):', bd.aspect_ratio_portrait],
['Aspect ratio (landscape):', bd.aspect_ratio_landscape],
['Display gamma:', '%.2f' % bd.display_gamma],
['DPM standby supported:', bd.dpm_standby],
['DPM suspend supported:', bd.dpm_suspend],
['DPM active-off supported:', bd.active_off],
['Display color type:', bd.display_type],
['sRGB Standard is default colour space:', bd.srgb_as_default],
[pix_str, bd.native_preferred_timing_mode]
]
PrintList(info, mode, ' %-50s %s')
cfs = bd.cont_freq_support
if mode == VERBOSE_MODE and cfs:
print(' %-50s %s' % ('Continuous frequency supported:', cfs))
def GetChromaticity(e, mode, raw_mode):
"""Prints and interprets the chromaticity information of an EDID.
Args:
e: The EDID being parsed.
mode: The level of verbosity for analysis.
raw_mode: The type of raw data print out, if any.
"""
print('[Chromaticity information]')
if raw_mode:
PrintRawRange(e.GetData(), raw_mode, 0x19, 0x23)
if mode == LAYOUT_MODE:
return
chrom = e.chromaticity
info = [
('Red', chrom.red_x, chrom.red_y),
('Green', chrom.grn_x, chrom.grn_y),
('Blue', chrom.blue_x, chrom.blue_y),
('White', chrom.wht_x, chrom.wht_y)
]
for x in info:
print('%7s: (%3d, %3d)' % x)
def GetEstablishedTiming(e, mode, raw_mode):
"""Prints and interprets the established timing information of an EDID.
Args:
e: The EDID being parsed.
mode: The level of verbosity for analysis.
raw_mode: The type of raw data print out, if any.
"""
print('[Established timing bitmap]')
if raw_mode:
PrintRawRange(e.GetData(), raw_mode, 0x23, 0x26)
if mode == LAYOUT_MODE:
return
et = e.established_timings
results = tools.ListTrueOnly(et.supported_timings)
if not results: # If empty list was returned
print(' None')
else:
for r in results:
if '@' in r:
res, hz = r.split('@')
print(' %-12s @%s' % (res, hz))
else:
print(' %-12s' % r)
def GetBaseStandardTiming(e, mode, raw_mode):
"""Prints and interprets the standard timing information of an EDID.
Args:
e: The EDID being parsed.
mode: The level of verbosity for analysis.
raw_mode: The type of raw data print out, if any.
"""
print('[Standard timing information]')
if raw_mode:
PrintRawRange(e.GetData(), raw_mode, 0x26, 0x36)
if mode == LAYOUT_MODE:
return
sts = e.standard_timings
if sts:
for st in sts:
PrintSt(st)
elif mode == VERBOSE_MODE:
print(' None')
def PrintSt(st):
"""Prints out information in a single standard_timings.StandardTiming object.
Args:
st: A standard_timings.StandardTiming object.
"""
x_res = st.x_resolution
rat = st.xy_pixel_ratio
num, denum = list(map(int, rat.split(':')))
y_res = (x_res * denum) / num
freq = st.vertical_freq
form_rat = '(%s)' % rat
print(' %4d x %4d %-8s @ %d Hz' % (x_res, y_res, form_rat, freq))
def GetDescriptorBlocks(e, mode, raw_mode):
"""Prints and interprets the descriptor blocks information of an EDID.
Calls PrintBlockAnalysis on each block for detailed print out.
Args:
e: The EDID being parsed.
mode: The level of verbosity for analysis.
raw_mode: The type of raw data print out, if any.
"""
print('[Descriptor blocks 1-4]')
descs = e.descriptors
for x in range(0, len(descs)):
prefix = 'Block #%d: ' % (x+1)
PrintBlockAnalysis(e, descs[x], mode, raw_mode, x, prefix)
PrintSpace(mode)
def PrintBlockAnalysis(e, desc, mode, raw_mode, start, prefix=None):
"""Prints and interprets a single 18-byte descriptor's information.
Called up to 4 times in a base EDID.
Uses descriptor module to determine descriptor type.
Args:
e: The full EDID being parsed.
desc: The descriptor being parsed.
mode: The level of verbosity for analysis.
raw_mode: The type of raw data print out, if any.
start: The start index of the descriptor.
prefix: Optional string description of which (nth) descriptor this is within
the EDID.
"""
print('%s%s' % (prefix, desc.type))
if mode == LAYOUT_MODE:
if desc.type == descriptor.TYPE_DISPLAY_RANGE_LIMITS:
print(' Subtype: %s' % desc.subtype)
if raw_mode:
base = 54
PrintRawRange(e.GetData(), raw_mode, base + (start * 18),
base + ((start + 1) * 18))
if mode == LAYOUT_MODE:
return
if (desc.type == descriptor.TYPE_PRODUCT_SERIAL_NUMBER or
desc.type == descriptor.TYPE_ALPHANUM_DATA_STRING or
desc.type == descriptor.TYPE_DISPLAY_PRODUCT_NAME):
print(' Data string:\t%s' % desc.string)
elif desc.type == descriptor.TYPE_DISPLAY_RANGE_LIMITS:
print('Subtype:', desc.subtype)
vert_rate = '%2d - %d' % (desc.min_vertical_rate,
desc.max_vertical_rate)
hor_rate = '%2d - %d' % (desc.min_horizontal_rate,
desc.max_horizontal_rate)
info = [
['Vertical rate (Hz):', vert_rate],
['Horizontal rate (kHz):', hor_rate],
['Pixel clock (MHz):', desc.pixel_clock]
]
PrintList(info, mode, ' %-35s %s')
if desc.subtype == descriptor.SUBTYPE_DISPLAY_RANGE_CVT:
ss = []
for ar in tools.ListTrueOnly(desc.supported_aspect_ratios):
ss.append(' %-35s %s' % ('', ar))
asp = '\n'.join(ss)
ss = []
for cb in tools.ListTrueOnly(desc.cvt_blanking_support):
ss.append(' %-35s %s' % ('', cb))
cvt_blank = '\n'.join(ss)
ss = []
for ds in tools.ListTrueOnly(desc.display_scaling_support):
ss.append(' %-35s %s' % ('', ds))
dis_scal = '\n'.join(ss)
cvt_info = [
['CVT Version:', desc.cvt_version],
['Additional Pixel Clock:',
'%s MHz' % desc.additional_pixel_clock],
['Maximum active pixels:', desc.max_active_pixels],
['Supported aspect ratios:', asp.strip()],
['Preferred aspect ratio:', desc.preferred_aspect_ratio],
['CVT blanking support:', cvt_blank.strip()],
['Display scaling support:', dis_scal.strip()],
['Preferred vertical refresh (Hz):', desc.preferred_vert_refresh]
]
PrintList(cvt_info, mode, ' %-35s %s')
elif desc.subtype == descriptor.SUBTYPE_DISPLAY_RANGE_2ND_GTF:
gtf_info = [
['Start break frequency:', desc.start_break_freq],
['C:', desc.c],
['M:', desc.m],
['K:', desc.k],
['J:', desc.j]
]
PrintList(gtf_info, mode, ' %-25s %s')
elif desc.type == descriptor.TYPE_COLOR_POINT_DATA:
cp_1 = desc.first_color_point
cp_2 = desc.second_color_point
PrintCp(cp_1, 1)
PrintCp(cp_2, 2)
elif desc.type == descriptor.TYPE_STANDARD_TIMING:
sts = desc.standard_timings
for st in sts:
PrintSt(st)
elif desc.type == descriptor.TYPE_DISPLAY_COLOR_MANAGEMENT:
dcm_info = [
['Red a3:', desc.red_a3],
['Red a2:', desc.red_a2],
['Green a3:', desc.green_a3],
['Green a2:', desc.green_a2],
['Blue a3:', desc.blue_a3],
['Blue a2:', desc.blue_a2]
]
PrintList(dcm_info, mode, ' %-12s %s')
elif desc.type == descriptor.TYPE_CVT_TIMING:
cvts = desc.coordinated_video_timings
for cvt in cvts:
PrintCvt(cvt)
elif desc.type == descriptor.TYPE_ESTABLISHED_TIMINGS_III:
print(tools.ListTrueOnly(desc.established_timings))
elif desc.type == descriptor.TYPE_MANUFACTURER_SPECIFIED:
if raw_mode:
print(desc.GetBlob())
elif desc.type == descriptor.TYPE_DETAILED_TIMING:
PrintDtd(desc)
def PrintCp(cp, num):
"""Prints out information about a single descriptor.ColorPoint object.
Args:
cp: A descriptor.ColorPoint object.
num: The index of the object (1st or 2nd).
"""
print('Color Point %d' % num)
wht = '(%d, %d)' % (cp.white_x, cp.white_y)
cp_info = [
['Index number:', cp.index_number],
['White point coordinates:', wht],
['Gamma:', str(cp.gamma) if cp.gamma else 'Gamma not described here']
]
PrintList(cp_info, VERBOSE_MODE, ' %-30s %s')
def PrintDtd(desc):
"""Prints out information about a single descriptor.DetailedTimingDescriptor.
Used in the base EDID analysis as well as certain extensions (i.e., VTB).
Args:
desc: The descriptor.DetailedTimingDescriptor.
"""
pix = '%.1f MHz' % (desc.pixel_clock)
active = '%d x %d' % (desc.h_active_pixels, desc.v_active_lines)
blank = '%d x %d' % (desc.h_blanking_pixels,
desc.v_blanking_lines)
fp = '%d x %d' % (desc.h_sync_offset, desc.v_sync_offset)
sp = '%d x %d' % (desc.h_sync_pulse, desc.v_sync_pulse)
ds = '%d x %d' % (desc.h_display_size, desc.v_display_size)
bord = '%d x %d' % (desc.h_border_pixels, desc.v_border_lines)
info = [
['Pixel clock:', pix],
['Addressable:', active],
['Blanking:', blank],
['Front porch:', fp],
['Sync pulse:', sp],
['Image size (mm):', ds],
['Border:', bord],
['Interlace:', desc.interlaced],
['Stereo viewing:', desc.stereo_mode]
]
PrintList(info, VERBOSE_MODE, ' %-17s %s')
st = desc.sync_type
print(' Sync type:')
for x in st:
print(' %-39s %s' % ('%s:' % x, st[x]))
def AnalyzeExtension(e, mode, raw_mode, block_num=1):
"""Prints and interprets an extension of an EDID.
Args:
e: The EDID being parsed.
mode: The level of verbosity for analysis.
raw_mode: The type of raw data print out, if any.
block_num: The index of the extension being analyzed (default: 1st ext).
"""
ext = e.GetExtension(block_num)
print('EXTENSION NUMBER %d: %s' % (block_num, ext.type))
if mode == VERBOSE_MODE:
print('Tag: %d' % ext.tag)
if ext.type == extensions.TYPE_CEA_861:
dbs = ext.data_blocks
dtds = ext.dtds
if mode == LAYOUT_MODE:
print('Number of data blocks: %d' % len(dbs))
for x in range(0, len(dbs)):
print('%d. %s' % (x+1, dbs[x].type))
# Calls PrintRawRange, but it'll only print if raw_mode on
PrintRawRange(dbs[x].GetBlock(), raw_mode)
PrintSpace(mode)
print('Number of detailed timing descriptors: %d' % len(dtds))
for x in range(0, len(dtds)):
print('%d. %s' % (x+1, dtds[x].type))
# Calls PrintRawRange, but it'll only print if raw_mode on
PrintRawRange(dtds[x].GetBlock(), raw_mode)
PrintSpace(mode)
return
# BASIC CEA INFO
if raw_mode:
cea_base = ext.GetBlock()[0:4]
PrintRawRange(cea_base, raw_mode)
cea_info = [
['Version:', ext.version],
['Underscan support:', ext.underscan_support],
['Basic audio support:', ext.basic_audio_support],
['YCbCr 4:4:4 support:', ext.ycbcr444_support],
['YCbCr 4:2:2 support:', ext.ycbcr422_support],
['Native DTD count:', ext.native_dtd_count]
]
PrintList(cea_info, mode, ' %-23s %s')
PrintSpace(mode)
# DATA BLOCKS
for db in dbs:
print('Data Block %s' % db.type)
if raw_mode:
PrintRawRange(db.GetBlock(), raw_mode)
db_basic = [
['Tag:', db.tag],
['Length:', db.length],
['Extension tag:', db.ext_tag],
]
if mode == VERBOSE_MODE:
PrintList(db_basic, mode, ' %-19s %s')
print('\n')
if (db.type == data_block.DB_TYPE_VIDEO or
db.type == data_block.DB_TYPE_YCBCR420_VIDEO):
svds = db.short_video_descriptors
for svd in svds:
print(' %-20s%s' % (svd.nativity, video_block.GetSvd(svd.vic)))
elif db.type == data_block.DB_TYPE_AUDIO:
ads = db.short_audio_descriptors
for x in range(0, len(ads)):
ad = ads[x]
print('Short audio descriptor #%d' % (x + 1))
ssf = tools.ListTrueOnly(ad.supported_sampling_freqs)
ad_basic = [
['Type:', ad.type],
['Max channel count:', ad.max_channel_count],
['Supported sampling:', ' '.join(ssf)],
]
if mode == VERBOSE_MODE:
ad_basic.insert(0, ['Format code:', ad.format_code])
PrintList(ad_basic, mode, ' %-19s %s')
if ad.type == data_block.AUDIO_TYPE_LPCM:
print(' %-19s %s' % ('Bit depth:',
tools.ListTrueOnly(ad.bit_depth)))
elif ad.type == data_block.AUDIO_TYPE_DRA:
print(' %-19s %s' % ('DRA value:', ad.value))
elif ad.format_code <= 8 and ad.format_code >= 2:
print(' %-19s %s' % ('Max bit rate:', ad.max_bit_rate))
elif ad.format_code <= 14 and ad.format_code <= 9:
print(' %-19s %s' % ('Value:', ad.value))
else:
print(' %-19s %s' % ('Extension code:', ad.ext_code))
print(' %-19s %s' % ('Frame length:', ad.frame_length))
print(' %-19s %s' % ('MPS support:', ad.mps_support))
print('\n')
elif db.type == data_block.DB_TYPE_SPEAKER_ALLOCATION:
print(' Speaker allocation:')
for a in tools.ListTrueOnly(db.allocation):
print(' %s' % a)
elif (db.type == data_block.DB_TYPE_VENDOR_SPECIFIC or
db.type == data_block.DB_TYPE_VENDOR_SPECIFIC_AUDIO or
db.type == data_block.DB_TYPE_VENDOR_SPECIFIC_VIDEO):
print(' %-20s %s' % ('IEEE:', db.ieee_oui))
print(' %-20s %s' % ('Data payload:', db.payload))
elif db.type == data_block.DB_TYPE_COLORIMETRY:
print(' Colorimetry:')
for c in tools.ListTrueOnly(db.colorimetry):
print(' %s' % c)
print(' %-20s %s' % ('Metadata:', db.metadata))
elif db.type == data_block.DB_TYPE_VIDEO_CAPABILITY:
vc_info = [
['YCC Quantization range:',
db.selectable_quantization_range_ycc],
['RGB Quantization range:',
db.selectable_quantization_range_rgb],
['PT behavior:', db.pt_behavior],
['IT behavior:', db.it_behavior],
['CE behavior:', db.ce_behavior]
]
PrintList(vc_info, mode, ' %-35s %s')
elif db.type == data_block.DB_TYPE_INFO_FRAME:
if_proc = db.if_processing
vsifs = db.vsifs
print(' %-25s %s' % ('VSIF count:', len(vsifs)))
if if_proc.payload:
print(' %-25s %s' % ('InfoFrame Processing Descriptor Payload:',
if_proc.payload))
for vsif in vsifs:
if mode == NORMAL_MODE:
v_type = vsif.type
print(' %-25s %s' % ('Type:', v_type))
if v_type == data_block.INFO_FRAME_TYPE_VENDOR_SPECIFIC:
print(' %-25s %s' % ('IEEE:', vsif.ieee_oui))
continue
vsif_info = [
['Type code:', vsif.type_code],
['Type:', vsif.type],
['Payload length:', vsif.payload_length],
['Data payload:', vsif.payload]
]
PrintList(vsif_info, mode, ' %-25s %s')
if vsif.type == data_block.INFO_FRAME_TYPE_VENDOR_SPECIFIC:
print(' %-25s %s' % ('IEEE:', vsif.ieee_oui))
elif db.type == data_block.DB_TYPE_YCBCR420_CAPABILITY_MAP:
sdis = db.supported_descriptor_indices
for sdi in sdis:
print(sdi)
elif db.type == data_block.DB_TYPE_VIDEO_FORMAT_PREFERENCE:
vps = db.video_preferences
for vp in vps:
if vp.type == data_block.VIDEO_PREFERENCE_VIC:
print(video_block.GetSvd(vp.vic))
elif vp.type == data_block.VIDEO_PREFERENCE_DTD:
print('%d-th DTD in EDID' % vp.dtd_index)
elif vp.type == data_block.VIDEO_PREFERENCE_RESERVED:
print('Reserved: %d' % vp.svr)
PrintSpace()
# DETAILED TIMING DESCRIPTORS
for x in range(0, len(dtds)):
print('Detailed Timing Descriptor #%d:' % (x + 1))
if raw_mode:
PrintRawRange(dtds[x].GetBlock(), raw_mode)
PrintDtd(dtds[x])
PrintSpace(mode)
elif ext.type == extensions.TYPE_VIDEO_TIMING_BLOCK:
dtbs = ext.dtbs
cvts = ext.cvts
sts = ext.sts
if mode == LAYOUT_MODE:
print('Number of detailed timing descriptors: %d' % len(dtbs))
for x in range(0, len(dtbs)):
print('%d. %s' % (x+1, dtbs[x].type))
this_dtb = dtbs[x].GetBlock()
PrintRawRange(this_dtb, raw_mode)
print('Number of coordinated video timing blocks: %d' % len(cvts))
for x in range(0, len(cvts)):
print('%d. Coordinated Video Timing Block' % (x + 1))
this_cvt = cvts[x].GetBlock()
PrintRawRange(this_cvt, raw_mode)
print('Number of standard timing blocks: %d' % len(sts))
for x in range(0, len(sts)):
print('%d. Standard Timing Block' % (x + 1))
this_st = sts[x].GetBlock()
PrintRawRange(this_st, raw_mode)
return
if raw_mode:
vtb_base = ext.GetBlock()[0:4]
PrintRawRange(vtb_base, raw_mode)
ext_basic = [
['Version:', ext.version],
['Number of DTBs:', ext.dtb_count],
['Number of CVTs:', ext.cvt_count],
['Number of STs:', ext.st_count]
]
PrintList(ext_basic, mode, '%-35s %s')
for dtb in dtbs:
print('\n\nDTD')
if raw_mode:
PrintRawRange(dtb.GetBlock(), raw_mode)
PrintDtd(dtb)
for cvt in cvts:
print('\n\nCVT')
if raw_mode:
PrintRawRange(cvt.GetBlock(), raw_mode)
PrintCvt(cvt)
for st in sts:
print('\n\nSTs')
if raw_mode:
PrintRawRange(st.GetBlock(), raw_mode)
PrintSt(st)
elif ext.type == extensions.TYPE_EXTENSION_BLOCK_MAP:
if raw_mode:
PrintRawRange(ext.GetBlock(), raw_mode)
if mode == LAYOUT_MODE:
return
tags = ext.all_tags
for x in range(0, len(tags)):
if tags[x]: # Not 0, bc 0 indicates unused block
print('Block %d: %d' % (x + block_num + 1, tags[x]))
def PrintCvt(cvt):
"""Prints out information about a single CoordinatedVideoTiming object.
Full object name: coordinated_video_timings.CoordinatedVideoTiming.
Args:
cvt: A single CoordinatedVideoTiming object.
"""
svr = tools.ListTrueOnly(cvt.supported_vertical_rates)
cvt_info = [
['Active vertical lines:', cvt.active_vertical_lines],
['Aspect ratio:', cvt.aspect_ratio],
['Preferred refresh rate:', cvt.preferred_vertical_rate],
['Supported refresh rates:', ' '.join(svr)]
]
if cvt:
PrintList(cvt_info, VERBOSE_MODE, ' %-35s %s')
else:
print('UNUSED FIELD')
def PrintBase(e, mode, raw_mode, types):
"""Prints and interprets all information of the base EDID.
Args:
e: The EDID being parsed.
mode: The level of verbosity for analysis.
raw_mode: The type of raw data print out, if any.
types: The list of types being analyzed.
"""
print('BASE EDID:')
if TYPE_BASE in types: # If TYPE_ALL was specified, BASE would be added in
types = [TYPE_VENDOR, TYPE_BD, TYPE_DCC, TYPE_ET, TYPE_ST, TYPE_DP]
# The following are no longer mutually exclusive
if TYPE_VENDOR in types:
# Note: Vendor info is very brief; no consideration for modes
GetManufacturerInfo(e, mode, raw_mode)
PrintSpace()
if TYPE_BD in types:
GetBasicDisplay(e, mode, raw_mode)
PrintSpace()
if TYPE_DCC in types:
GetChromaticity(e, mode, raw_mode)
PrintSpace()
if TYPE_ET in types:
GetEstablishedTiming(e, mode, raw_mode)
PrintSpace()
if TYPE_ST in types:
GetBaseStandardTiming(e, mode, raw_mode)
PrintSpace()
if TYPE_DP in types:
GetDescriptorBlocks(e, mode, raw_mode)
PrintSpace()
def PrintExtensions(e, mode, raw_mode, exts):
"""Prints and interprets all information of one or more extensions.
Args:
e: The EDID being parsed.
mode: The level of verbosity for analysis.
raw_mode: The type of raw data print out, if any.
exts: The list of extensions to be analyzed.
"""
if TYPE_XALL in exts:
GetXall(e, mode, raw_mode)
else:
for ext in exts:
block_num = int(ext[1:])
AnalyzeExtension(e, mode, raw_mode, block_num)
PrintSpace()
def GetXall(e, mode, raw_mode):
"""Prints and interprets the information of all extensions to an EDID.
Args:
e: The EDID being parsed.
mode: The level of verbosity for analysis.
raw_mode: The type of raw data print out, if any.
"""
num_ext = e.extension_count
for x in range(1, num_ext + 1):
AnalyzeExtension(e, mode, raw_mode, x)
PrintSpace(mode)
def PrintRawRange(e, raw_mode, start=0, end=None):
"""Prints the raw data of a section of an EDID.
If no arguments given for start and end, the whole list is printed.
Args:
e: The list of bytes being printed.
raw_mode: The type of raw data print out - hex, decimal, or none.
start: The index of the first byte to print.
end: The index of the last byte NOT to print.
"""
data = e[start:end]
if raw_mode:
if raw_mode == RAW_HEX:
my_format = ' Byte 0x%02X:\t0x%02X'
else: # Decimal
my_format = ' Byte %04d:\t%04d'
for x in range(0, len(data)):
print(my_format % (x + start, data[x]))
print('\n')
def PrintHexEdid(e):
"""Prints the entire EDID in hexadecimal form.
Args:
e: The EDID to be printed.
"""
data = e.GetData()
hex_rows = len(data) / 16
print('\t\t 0 1 2 3 4 5 6 7 8 9 A B C D E F')
for x in range(0, hex_rows):
start = 0x10 * x
row = '%02X%02X ' * 8 % tuple(data[start : start + 16])
print('0x%04X:\t\t%s' % (x, row))
def PrintDecEdid(e):
"""Prints the entire EDID in decimal form.
Args:
e: The EDID to be printed.
"""
# Decimal
data = e.GetData()
dec_rows = len(data) / 8
for x in range(0, dec_rows):
start = 8 * x
row = '%04d %04d ' * 4 % tuple(data[start : start + 8])
line_range = '%d-%d:' % (start, start + 7)
print('%9s\t%s' % (line_range, row))
def Verify(e):
"""Error checks the EDID.
Args:
e: The EDID for error checking.
"""
errors = e.GetErrors()
print('Found %d errors\n\n' % len(errors))
# Can format this better later
for x in range(0, len(errors)):
print('ERROR %d' % (x + 1))
print(errors[x].location)
print(errors[x].message)
if errors[x].expected:
print('\tExpected\t%s' % errors[x].expected)
if errors[x].found:
print('\tFound\t\t\t%s' % errors[x].found)
PrintSpace(0)
def Version(e):
"""Prints the EDID version.
Args:
e: The EDID being analyzed.
"""
print('EDID version: %s' % e.edid_version)
def Xc(e):
"""Prints the number of extensions (extension count).
Args:
e: The EDID being analyzed.
"""
print('Extension count: %d' % e.extension_count)
def CheckInvalidTypes(types, exts):
"""Checks the types listed for the parse subcommand for validity.
Args:
types: The list of strings indicating sections of base EDID to parse.
exts: The list of strings indicating sections of extensions to parse.
"""
for t in types:
if t not in VALID_TYPES and t not in exts:
print(('Error: %s is not a valid type. %s')
% (t, VALID_TYPE_MESSAGE))
PrintSpace()
def ParseEdid():
"""Parses an EDID and prints its info according to commands and flags."""
p = argparse.ArgumentParser(description='Select sections of EDID to parse.')
sp = p.add_subparsers(title='subcommands', description='valid subcommands',
metavar='')
sp_verify = sp.add_parser('verify', help='Error check the EDID')
sp_verify.set_defaults(func=Verify)
sp_version = sp.add_parser('version', help='Print EDID version')
sp_version.set_defaults(func=Version)
sp_hex = sp.add_parser('hex', help='Print full EDID in hex')
sp_hex.set_defaults(func=PrintHexEdid)
sp_dec = sp.add_parser('dec', help='Print full EDID in decimal')
sp_dec.set_defaults(func=PrintDecEdid)
sp_xc = sp.add_parser('xc', help='Print extension count')
sp_xc.set_defaults(func=Xc)
sp_parse = sp.add_parser('parse', help='Parse full or sections of EDID.'
' Run \'./edidparser.py parse -h\' for more info '
'on additional arguments.')
# The following are all arguments for the parse subcommand
parse_mode = sp_parse.add_mutually_exclusive_group()
parse_mode.add_argument('-v', '--verbose', action='store_true',
help='Show detailed information')
parse_mode.add_argument('-n', '--normal', action='store_true',
help='Show standard information')
parse_mode.add_argument('-l', '--layout', action='store_true',
help='Show basic layout information')
parse_raw = sp_parse.add_mutually_exclusive_group()
parse_raw.add_argument('--hex', action='store_true',
help='Print raw data for each section in hex')
parse_raw.add_argument('--dec', action='store_true',
help='Print raw data for each section in decimal')
sp_parse.add_argument('-t', '--types', type=str, help=type_help_string)
# Positional arguments: 1) EDID name
p.add_argument('edid_name', type=str,
help='Name of EDID binary blob for parsing')
args = p.parse_args()
# Fill the edid list with bytes from binary blob
e = edid.Edid(BytesFromFile(args.edid_name))
print('Parsing %s' % args.edid_name)
if hasattr(args, 'func'): # Not the 'parse' subcommand
args.func(e)
exit()
# Set defaults here
mode = NORMAL_MODE
raw_mode = RAW_OFF
if args.layout: # Layout of EDID only
# Here, set the mode to layout mode
# Run each analysis method but get back basic info only
mode = LAYOUT_MODE
elif args.verbose:
# Here, set the mode to verbose mode
mode = VERBOSE_MODE
# Otherwise, mode remains NORMAL_MODE
if args.dec:
raw_mode = RAW_DEC
elif args.hex:
raw_mode = RAW_HEX
if not args.types or TYPE_ALL in args.types.split(','):
base_types = [TYPE_BASE]
ext_types = [TYPE_XALL]
else:
base_types = args.types.split(',')
regex = re.compile('(x).*')
ext_types = [m.group(0) for t in base_types for m in [regex.search(t)] if m]
CheckInvalidTypes(base_types, ext_types)
print('EDID version: %s' % e.edid_version)
PrintSpace()
PrintBase(e, mode, raw_mode, base_types)
PrintExtensions(e, mode, raw_mode, ext_types)
####################
# CODE STARTS HERE #
####################
if __name__ == '__main__':
ParseEdid()