1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/display/util/edid_parser.h"
10 #include "base/strings/string_util.h"
11 #include "base/sys_byteorder.h"
17 // Returns 64-bit persistent ID for the specified manufacturer's ID and
18 // product_code_hash, and the index of the output it is connected to.
19 // |output_index| is used to distinguish the displays of the same type. For
20 // example, swapping two identical display between two outputs will not be
21 // treated as swap. The 'serial number' field in EDID isn't used here because
22 // it is not guaranteed to have unique number and it may have the same fixed
24 int64_t GetID(uint16_t manufacturer_id
,
25 uint32_t product_code_hash
,
26 uint8_t output_index
) {
27 return ((static_cast<int64_t>(manufacturer_id
) << 40) |
28 (static_cast<int64_t>(product_code_hash
) << 8) | output_index
);
33 bool GetDisplayIdFromEDID(const std::vector
<uint8_t>& edid
,
35 int64_t* display_id_out
) {
36 uint16_t manufacturer_id
= 0;
37 std::string product_name
;
39 // ParseOutputDeviceData fails if it doesn't have product_name.
40 ParseOutputDeviceData(edid
, &manufacturer_id
, &product_name
);
42 // Generates product specific value from product_name instead of product code.
43 // See crbug.com/240341
44 uint32_t product_code_hash
= product_name
.empty() ?
45 0 : base::Hash(product_name
);
46 if (manufacturer_id
!= 0) {
47 // An ID based on display's index will be assigned later if this call
49 *display_id_out
= GetID(
50 manufacturer_id
, product_code_hash
, output_index
);
56 bool ParseOutputDeviceData(const std::vector
<uint8_t>& edid
,
57 uint16_t* manufacturer_id
,
58 std::string
* human_readable_name
) {
59 // See http://en.wikipedia.org/wiki/Extended_display_identification_data
60 // for the details of EDID data format. We use the following data:
61 // bytes 8-9: manufacturer EISA ID, in big-endian
62 // bytes 54-125: four descriptors (18-bytes each) which may contain
64 const unsigned int kManufacturerOffset
= 8;
65 const unsigned int kManufacturerLength
= 2;
66 const unsigned int kDescriptorOffset
= 54;
67 const unsigned int kNumDescriptors
= 4;
68 const unsigned int kDescriptorLength
= 18;
69 // The specifier types.
70 const unsigned char kMonitorNameDescriptor
= 0xfc;
72 if (manufacturer_id
) {
73 if (edid
.size() < kManufacturerOffset
+ kManufacturerLength
) {
74 LOG(ERROR
) << "too short EDID data: manifacturer id";
79 *reinterpret_cast<const uint16_t*>(&edid
[kManufacturerOffset
]);
80 #if defined(ARCH_CPU_LITTLE_ENDIAN)
81 *manufacturer_id
= base::ByteSwap(*manufacturer_id
);
85 if (!human_readable_name
)
88 human_readable_name
->clear();
89 for (unsigned int i
= 0; i
< kNumDescriptors
; ++i
) {
90 if (edid
.size() < kDescriptorOffset
+ (i
+ 1) * kDescriptorLength
)
93 size_t offset
= kDescriptorOffset
+ i
* kDescriptorLength
;
94 // If the descriptor contains the display name, it has the following
97 // byte 3: descriptor type, defined above.
98 // bytes 5-17: text data, ending with \r, padding with spaces
99 // we should check bytes 0-2 and 4, since it may have other values in
100 // case that the descriptor contains other type of data.
101 if (edid
[offset
] == 0 && edid
[offset
+ 1] == 0 && edid
[offset
+ 2] == 0 &&
102 edid
[offset
+ 3] == kMonitorNameDescriptor
&& edid
[offset
+ 4] == 0) {
103 std::string
found_name(reinterpret_cast<const char*>(&edid
[offset
+ 5]),
104 kDescriptorLength
- 5);
105 base::TrimWhitespaceASCII(
106 found_name
, base::TRIM_TRAILING
, human_readable_name
);
111 // Verify if the |human_readable_name| consists of printable characters only.
112 for (size_t i
= 0; i
< human_readable_name
->size(); ++i
) {
113 char c
= (*human_readable_name
)[i
];
114 if (!isascii(c
) || !isprint(c
)) {
115 human_readable_name
->clear();
116 LOG(ERROR
) << "invalid EDID: human unreadable char in name";
124 bool ParseOutputOverscanFlag(const std::vector
<uint8_t>& edid
,
126 // See http://en.wikipedia.org/wiki/Extended_display_identification_data
127 // for the extension format of EDID. Also see EIA/CEA-861 spec for
128 // the format of the extensions and how video capability is encoded.
129 // - byte 0: tag. should be 02h.
130 // - byte 1: revision. only cares revision 3 (03h).
131 // - byte 4-: data block.
132 const unsigned int kExtensionBase
= 128;
133 const unsigned int kExtensionSize
= 128;
134 const unsigned int kNumExtensionsOffset
= 126;
135 const unsigned int kDataBlockOffset
= 4;
136 const unsigned char kCEAExtensionTag
= '\x02';
137 const unsigned char kExpectedExtensionRevision
= '\x03';
138 const unsigned char kExtendedTag
= 7;
139 const unsigned char kExtendedVideoCapabilityTag
= 0;
140 const unsigned int kPTOverscan
= 4;
141 const unsigned int kITOverscan
= 2;
142 const unsigned int kCEOverscan
= 0;
144 if (edid
.size() <= kNumExtensionsOffset
)
147 unsigned char num_extensions
= edid
[kNumExtensionsOffset
];
149 for (size_t i
= 0; i
< num_extensions
; ++i
) {
150 // Skip parsing the whole extension if size is not enough.
151 if (edid
.size() < kExtensionBase
+ (i
+ 1) * kExtensionSize
)
154 size_t extension_offset
= kExtensionBase
+ i
* kExtensionSize
;
155 unsigned char tag
= edid
[extension_offset
];
156 unsigned char revision
= edid
[extension_offset
+ 1];
157 if (tag
!= kCEAExtensionTag
|| revision
!= kExpectedExtensionRevision
)
160 unsigned char timing_descriptors_start
= std::min(
161 edid
[extension_offset
+ 2], static_cast<unsigned char>(kExtensionSize
));
163 for (size_t data_offset
= extension_offset
+ kDataBlockOffset
;
164 data_offset
< extension_offset
+ timing_descriptors_start
;) {
165 // A data block is encoded as:
166 // - byte 1 high 3 bits: tag. '07' for extended tags.
167 // - byte 1 remaining bits: the length of data block.
168 // - byte 2: the extended tag. '0' for video capability.
169 // - byte 3: the capability.
170 unsigned char tag
= edid
[data_offset
] >> 5;
171 unsigned char payload_length
= edid
[data_offset
] & 0x1f;
172 if (data_offset
+ payload_length
> edid
.size())
175 if (tag
!= kExtendedTag
|| payload_length
< 2 ||
176 edid
[data_offset
+ 1] != kExtendedVideoCapabilityTag
) {
177 data_offset
+= payload_length
+ 1;
181 // The difference between preferred, IT, and CE video formats
182 // doesn't matter. Sets |flag| to true if any of these flags are true.
183 if ((edid
[data_offset
+ 2] & (1 << kPTOverscan
)) ||
184 (edid
[data_offset
+ 2] & (1 << kITOverscan
)) ||
185 (edid
[data_offset
+ 2] & (1 << kCEOverscan
))) {