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"
12 #include "ui/gfx/geometry/size.h"
18 // Returns 64-bit persistent ID for the specified manufacturer's ID and
19 // product_code_hash, and the index of the output it is connected to.
20 // |output_index| is used to distinguish the displays of the same type. For
21 // example, swapping two identical display between two outputs will not be
22 // treated as swap. The 'serial number' field in EDID isn't used here because
23 // it is not guaranteed to have unique number and it may have the same fixed
25 int64_t GetID(uint16_t manufacturer_id
,
26 uint32_t product_code_hash
,
27 uint8_t output_index
) {
28 return ((static_cast<int64_t>(manufacturer_id
) << 40) |
29 (static_cast<int64_t>(product_code_hash
) << 8) | output_index
);
34 bool GetDisplayIdFromEDID(const std::vector
<uint8_t>& edid
,
36 int64_t* display_id_out
) {
37 uint16_t manufacturer_id
= 0;
38 std::string product_name
;
40 // ParseOutputDeviceData fails if it doesn't have product_name.
41 ParseOutputDeviceData(edid
, &manufacturer_id
, &product_name
, nullptr,
44 // Generates product specific value from product_name instead of product code.
45 // See crbug.com/240341
46 uint32_t product_code_hash
= product_name
.empty() ?
47 0 : base::Hash(product_name
);
48 if (manufacturer_id
!= 0) {
49 // An ID based on display's index will be assigned later if this call
51 *display_id_out
= GetID(
52 manufacturer_id
, product_code_hash
, output_index
);
58 bool ParseOutputDeviceData(const std::vector
<uint8_t>& edid
,
59 uint16_t* manufacturer_id
,
60 std::string
* human_readable_name
,
61 gfx::Size
* active_pixel_out
,
62 gfx::Size
* physical_display_size_out
) {
63 // See http://en.wikipedia.org/wiki/Extended_display_identification_data
64 // for the details of EDID data format. We use the following data:
65 // bytes 8-9: manufacturer EISA ID, in big-endian
66 // bytes 54-125: four descriptors (18-bytes each) which may contain
68 const unsigned int kManufacturerOffset
= 8;
69 const unsigned int kManufacturerLength
= 2;
70 const unsigned int kDescriptorOffset
= 54;
71 const unsigned int kNumDescriptors
= 4;
72 const unsigned int kDescriptorLength
= 18;
73 // The specifier types.
74 const unsigned char kMonitorNameDescriptor
= 0xfc;
76 if (manufacturer_id
) {
77 if (edid
.size() < kManufacturerOffset
+ kManufacturerLength
) {
78 LOG(ERROR
) << "too short EDID data: manifacturer id";
83 *reinterpret_cast<const uint16_t*>(&edid
[kManufacturerOffset
]);
84 #if defined(ARCH_CPU_LITTLE_ENDIAN)
85 *manufacturer_id
= base::ByteSwap(*manufacturer_id
);
89 if (human_readable_name
)
90 human_readable_name
->clear();
92 for (unsigned int i
= 0; i
< kNumDescriptors
; ++i
) {
93 if (edid
.size() < kDescriptorOffset
+ (i
+ 1) * kDescriptorLength
)
96 size_t offset
= kDescriptorOffset
+ i
* kDescriptorLength
;
98 // Detailed Timing Descriptor:
99 if (edid
[offset
] != 0 && edid
[offset
+ 1] != 0) {
100 const int kMaxResolution
= 10080; // 8k display.
102 if (active_pixel_out
) {
103 const int kHorizontalPixelLsbOffset
= 2;
104 const int kHorizontalPixelMsbOffset
= 4;
105 const int kVerticalPixelLsbOffset
= 5;
106 const int kVerticalPixelMsbOffset
= 7;
108 int h_lsb
= edid
[offset
+ kHorizontalPixelLsbOffset
];
109 int h_msb
= edid
[offset
+ kHorizontalPixelMsbOffset
];
110 int h_pixel
= std::min(h_lsb
+ ((h_msb
& 0xF0) << 4), kMaxResolution
);
112 int v_lsb
= edid
[offset
+ kVerticalPixelLsbOffset
];
113 int v_msb
= edid
[offset
+ kVerticalPixelMsbOffset
];
114 int v_pixel
= std::min(v_lsb
+ ((v_msb
& 0xF0) << 4), kMaxResolution
);
116 active_pixel_out
->SetSize(h_pixel
, v_pixel
);
117 // EDID may contain multiple DTD. Use first one that
118 // contains the highest resolution.
119 active_pixel_out
= nullptr;
122 if (physical_display_size_out
) {
123 const int kHorizontalSizeLsbOffset
= 12;
124 const int kVerticalSizeLsbOffset
= 13;
125 const int kSizeMsbOffset
= 14;
127 int h_lsb
= edid
[offset
+ kHorizontalSizeLsbOffset
];
128 int v_lsb
= edid
[offset
+ kVerticalSizeLsbOffset
];
130 int msb
= edid
[offset
+ kSizeMsbOffset
];
131 int h_size
= h_lsb
+ ((msb
& 0xF0) << 4);
132 int v_size
= v_lsb
+ ((msb
& 0x0F) << 8);
133 physical_display_size_out
->SetSize(h_size
, v_size
);
134 physical_display_size_out
= nullptr;
139 // EDID Other Monitor Descriptors:
140 // If the descriptor contains the display name, it has the following
143 // byte 3: descriptor type, defined above.
144 // bytes 5-17: text data, ending with \r, padding with spaces
145 // we should check bytes 0-2 and 4, since it may have other values in
146 // case that the descriptor contains other type of data.
147 if (edid
[offset
] == 0 && edid
[offset
+ 1] == 0 && edid
[offset
+ 2] == 0 &&
148 edid
[offset
+ 3] == kMonitorNameDescriptor
&& edid
[offset
+ 4] == 0 &&
149 human_readable_name
) {
150 std::string
found_name(reinterpret_cast<const char*>(&edid
[offset
+ 5]),
151 kDescriptorLength
- 5);
152 base::TrimWhitespaceASCII(
153 found_name
, base::TRIM_TRAILING
, human_readable_name
);
158 // Verify if the |human_readable_name| consists of printable characters only.
159 // TODO(oshima|muka): Consider replacing unprintable chars with white space.
160 if (human_readable_name
) {
161 for (size_t i
= 0; i
< human_readable_name
->size(); ++i
) {
162 char c
= (*human_readable_name
)[i
];
163 if (!isascii(c
) || !isprint(c
)) {
164 human_readable_name
->clear();
165 LOG(ERROR
) << "invalid EDID: human unreadable char in name";
174 bool ParseOutputOverscanFlag(const std::vector
<uint8_t>& edid
,
176 // See http://en.wikipedia.org/wiki/Extended_display_identification_data
177 // for the extension format of EDID. Also see EIA/CEA-861 spec for
178 // the format of the extensions and how video capability is encoded.
179 // - byte 0: tag. should be 02h.
180 // - byte 1: revision. only cares revision 3 (03h).
181 // - byte 4-: data block.
182 const unsigned int kExtensionBase
= 128;
183 const unsigned int kExtensionSize
= 128;
184 const unsigned int kNumExtensionsOffset
= 126;
185 const unsigned int kDataBlockOffset
= 4;
186 const unsigned char kCEAExtensionTag
= '\x02';
187 const unsigned char kExpectedExtensionRevision
= '\x03';
188 const unsigned char kExtendedTag
= 7;
189 const unsigned char kExtendedVideoCapabilityTag
= 0;
190 const unsigned int kPTOverscan
= 4;
191 const unsigned int kITOverscan
= 2;
192 const unsigned int kCEOverscan
= 0;
194 if (edid
.size() <= kNumExtensionsOffset
)
197 unsigned char num_extensions
= edid
[kNumExtensionsOffset
];
199 for (size_t i
= 0; i
< num_extensions
; ++i
) {
200 // Skip parsing the whole extension if size is not enough.
201 if (edid
.size() < kExtensionBase
+ (i
+ 1) * kExtensionSize
)
204 size_t extension_offset
= kExtensionBase
+ i
* kExtensionSize
;
205 unsigned char tag
= edid
[extension_offset
];
206 unsigned char revision
= edid
[extension_offset
+ 1];
207 if (tag
!= kCEAExtensionTag
|| revision
!= kExpectedExtensionRevision
)
210 unsigned char timing_descriptors_start
= std::min(
211 edid
[extension_offset
+ 2], static_cast<unsigned char>(kExtensionSize
));
213 for (size_t data_offset
= extension_offset
+ kDataBlockOffset
;
214 data_offset
< extension_offset
+ timing_descriptors_start
;) {
215 // A data block is encoded as:
216 // - byte 1 high 3 bits: tag. '07' for extended tags.
217 // - byte 1 remaining bits: the length of data block.
218 // - byte 2: the extended tag. '0' for video capability.
219 // - byte 3: the capability.
220 unsigned char tag
= edid
[data_offset
] >> 5;
221 unsigned char payload_length
= edid
[data_offset
] & 0x1f;
222 if (data_offset
+ payload_length
> edid
.size())
225 if (tag
!= kExtendedTag
|| payload_length
< 2 ||
226 edid
[data_offset
+ 1] != kExtendedVideoCapabilityTag
) {
227 data_offset
+= payload_length
+ 1;
231 // The difference between preferred, IT, and CE video formats
232 // doesn't matter. Sets |flag| to true if any of these flags are true.
233 if ((edid
[data_offset
+ 2] & (1 << kPTOverscan
)) ||
234 (edid
[data_offset
+ 2] & (1 << kITOverscan
)) ||
235 (edid
[data_offset
+ 2] & (1 << kCEOverscan
))) {