1*9c5db199SXin Li# Lint as: python2, python3 2*9c5db199SXin Li# Copyright (c) 2014 The Chromium OS Authors. All rights reserved. 3*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be 4*9c5db199SXin Li# found in the LICENSE file. 5*9c5db199SXin Li 6*9c5db199SXin Lifrom __future__ import absolute_import 7*9c5db199SXin Lifrom __future__ import division 8*9c5db199SXin Lifrom __future__ import print_function 9*9c5db199SXin Lifrom functools import reduce 10*9c5db199SXin Liimport codecs 11*9c5db199SXin Liimport logging 12*9c5db199SXin Liimport operator 13*9c5db199SXin Liimport os 14*9c5db199SXin Liimport six 15*9c5db199SXin Lifrom six.moves import map 16*9c5db199SXin Lifrom six.moves import range 17*9c5db199SXin Liimport sys 18*9c5db199SXin Li 19*9c5db199SXin Li 20*9c5db199SXin Li# TODO: This is a quick workaround; some of our arm devices so far only 21*9c5db199SXin Li# support the HDMI EDIDs and the DP one at 1680x1050. A more proper 22*9c5db199SXin Li# solution is to build a database of supported resolutions and pixel 23*9c5db199SXin Li# clocks for each model and check if the EDID is in the supported list. 24*9c5db199SXin Lidef is_edid_supported(host, width, height): 25*9c5db199SXin Li """Check whether the EDID is supported by DUT 26*9c5db199SXin Li 27*9c5db199SXin Li @param host: A CrosHost object. 28*9c5db199SXin Li @param width: The screen width 29*9c5db199SXin Li @param height: The screen height 30*9c5db199SXin Li 31*9c5db199SXin Li @return: True if the check passes; False otherwise. 32*9c5db199SXin Li """ 33*9c5db199SXin Li # TODO: Support client test that the host is not a CrosHost. 34*9c5db199SXin Li platform = host.get_platform() 35*9c5db199SXin Li if platform in ('snow', 'spring', 'skate', 'peach_pi', 'veyron_jerry'): 36*9c5db199SXin Li if (width, height) in [(1280, 800), (1440, 900), (1600, 900), 37*9c5db199SXin Li (3840, 2160)]: 38*9c5db199SXin Li return False 39*9c5db199SXin Li if platform in ('kahlee', 'grunt'): 40*9c5db199SXin Li if (width, height) in [(3840, 2160)]: 41*9c5db199SXin Li return False 42*9c5db199SXin Li return True 43*9c5db199SXin Li 44*9c5db199SXin Li 45*9c5db199SXin Liclass Edid(object): 46*9c5db199SXin Li """Edid is an abstraction of EDID (Extended Display Identification Data). 47*9c5db199SXin Li 48*9c5db199SXin Li It provides methods to get the properties, manipulate the structure, 49*9c5db199SXin Li import from a file, export to a file, etc. 50*9c5db199SXin Li 51*9c5db199SXin Li """ 52*9c5db199SXin Li 53*9c5db199SXin Li BLOCK_SIZE = 128 54*9c5db199SXin Li 55*9c5db199SXin Li 56*9c5db199SXin Li def __init__(self, data, skip_verify=False): 57*9c5db199SXin Li """Construct an Edid. 58*9c5db199SXin Li 59*9c5db199SXin Li @param data: A byte-array of EDID data. 60*9c5db199SXin Li @param skip_verify: True to skip the correctness check. 61*9c5db199SXin Li """ 62*9c5db199SXin Li if not Edid.verify(data) and not skip_verify: 63*9c5db199SXin Li raise ValueError('Not a valid EDID.') 64*9c5db199SXin Li self.data = data 65*9c5db199SXin Li 66*9c5db199SXin Li 67*9c5db199SXin Li @staticmethod 68*9c5db199SXin Li def verify(data): 69*9c5db199SXin Li """Verify the correctness of EDID. 70*9c5db199SXin Li 71*9c5db199SXin Li @param data: A byte-array of EDID data. 72*9c5db199SXin Li 73*9c5db199SXin Li @return True if the EDID is correct; False otherwise. 74*9c5db199SXin Li """ 75*9c5db199SXin Li data_len = len(data) 76*9c5db199SXin Li if data_len % Edid.BLOCK_SIZE != 0: 77*9c5db199SXin Li logging.debug('EDID has an invalid length: %d', data_len) 78*9c5db199SXin Li return False 79*9c5db199SXin Li 80*9c5db199SXin Li for start in range(0, data_len, Edid.BLOCK_SIZE): 81*9c5db199SXin Li # Each block (128-byte) has a checksum at the last byte. 82*9c5db199SXin Li if sys.version_info.major < 3: 83*9c5db199SXin Li checksum = reduce(operator.add, 84*9c5db199SXin Li list(map(ord, data[start:start+Edid.BLOCK_SIZE]))) 85*9c5db199SXin Li else: 86*9c5db199SXin Li checksum = reduce(operator.add, 87*9c5db199SXin Li list(data[start:start+Edid.BLOCK_SIZE])) 88*9c5db199SXin Li if checksum % 256 != 0: 89*9c5db199SXin Li logging.debug('Wrong checksum in the block %d of EDID', 90*9c5db199SXin Li start // Edid.BLOCK_SIZE) 91*9c5db199SXin Li return False 92*9c5db199SXin Li 93*9c5db199SXin Li return True 94*9c5db199SXin Li 95*9c5db199SXin Li 96*9c5db199SXin Li @classmethod 97*9c5db199SXin Li def from_file(cls, filename, skip_verify=False): 98*9c5db199SXin Li """Construct an Edid from a file. 99*9c5db199SXin Li 100*9c5db199SXin Li @param filename: A string of filename. 101*9c5db199SXin Li @param skip_verify: True to skip the correctness check. 102*9c5db199SXin Li """ 103*9c5db199SXin Li if not os.path.exists(filename): 104*9c5db199SXin Li raise ValueError('EDID file %r does not exist' % filename) 105*9c5db199SXin Li 106*9c5db199SXin Li if filename.upper().endswith('.TXT'): 107*9c5db199SXin Li # Convert the EDID text format, returning from xrandr. 108*9c5db199SXin Li data = reduce(operator.add, 109*9c5db199SXin Li [codecs.decode(s.strip(), 'hex') for s in open(filename).readlines()]) 110*9c5db199SXin Li else: 111*9c5db199SXin Li data = open(filename, 'rb').read() 112*9c5db199SXin Li return cls(data, skip_verify) 113*9c5db199SXin Li 114*9c5db199SXin Li 115*9c5db199SXin Li def to_file(self, filename): 116*9c5db199SXin Li """Export the EDID to a file. 117*9c5db199SXin Li 118*9c5db199SXin Li @param filename: A string of filename. 119*9c5db199SXin Li """ 120*9c5db199SXin Li with open(filename, 'w+') as f: 121*9c5db199SXin Li f.write(self.data) 122*9c5db199SXin Li 123*9c5db199SXin Li 124*9c5db199SXin Li# A constant object to represent no EDID. 125*9c5db199SXin LiNO_EDID = Edid('', skip_verify=True) 126