aboutsummaryrefslogtreecommitdiff
path: root/client/cros/chameleon/edid.py
blob: 9de0666110aa06851c444b3b19fe85c023a8e346 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# Lint as: python2, python3
# Copyright (c) 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.

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from functools import reduce
import codecs
import logging
import operator
import os
import six
from six.moves import map
from six.moves import range
import sys


# TODO: This is a quick workaround; some of our arm devices so far only
# support the HDMI EDIDs and the DP one at 1680x1050. A more proper
# solution is to build a database of supported resolutions and pixel
# clocks for each model and check if the EDID is in the supported list.
def is_edid_supported(host, width, height):
    """Check whether the EDID is supported by DUT

    @param host: A CrosHost object.
    @param width: The screen width
    @param height: The screen height

    @return: True if the check passes; False otherwise.
    """
    # TODO: Support client test that the host is not a CrosHost.
    platform = host.get_platform()
    if platform in ('snow', 'spring', 'skate', 'peach_pi', 'veyron_jerry'):
        if (width, height) in [(1280, 800), (1440, 900), (1600, 900),
                               (3840, 2160)]:
            return False
    if platform in ('kahlee', 'grunt'):
        if (width, height) in [(3840, 2160)]:
            return False
    return True


class Edid(object):
    """Edid is an abstraction of EDID (Extended Display Identification Data).

    It provides methods to get the properties, manipulate the structure,
    import from a file, export to a file, etc.

    """

    BLOCK_SIZE = 128


    def __init__(self, data, skip_verify=False):
        """Construct an Edid.

        @param data: A byte-array of EDID data.
        @param skip_verify: True to skip the correctness check.
        """
        if not Edid.verify(data) and not skip_verify:
            raise ValueError('Not a valid EDID.')
        self.data = data


    @staticmethod
    def verify(data):
        """Verify the correctness of EDID.

        @param data: A byte-array of EDID data.

        @return True if the EDID is correct; False otherwise.
        """
        data_len = len(data)
        if data_len % Edid.BLOCK_SIZE != 0:
            logging.debug('EDID has an invalid length: %d', data_len)
            return False

        for start in range(0, data_len, Edid.BLOCK_SIZE):
            # Each block (128-byte) has a checksum at the last byte.
            if sys.version_info.major < 3:
                checksum = reduce(operator.add,
                              list(map(ord, data[start:start+Edid.BLOCK_SIZE])))
            else:
                checksum = reduce(operator.add,
                                  list(data[start:start+Edid.BLOCK_SIZE]))
            if checksum % 256 != 0:
                logging.debug('Wrong checksum in the block %d of EDID',
                              start // Edid.BLOCK_SIZE)
                return False

        return True


    @classmethod
    def from_file(cls, filename, skip_verify=False):
        """Construct an Edid from a file.

        @param filename: A string of filename.
        @param skip_verify: True to skip the correctness check.
        """
        if not os.path.exists(filename):
            raise ValueError('EDID file %r does not exist' % filename)

        if filename.upper().endswith('.TXT'):
            # Convert the EDID text format, returning from xrandr.
            data = reduce(operator.add,
                          [codecs.decode(s.strip(), 'hex') for s in open(filename).readlines()])
        else:
            data = open(filename, 'rb').read()
        return cls(data, skip_verify)


    def to_file(self, filename):
        """Export the EDID to a file.

        @param filename: A string of filename.
        """
        with open(filename, 'w+') as f:
            f.write(self.data)


# A constant object to represent no EDID.
NO_EDID = Edid('', skip_verify=True)