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
);
32 // Returns a 32-bit identifier for this model of display, using
33 // |manufacturer_id| and |product_code|.
34 uint32_t GetProductID(uint16_t manufacturer_id
, uint16_t product_code
) {
35 return ((static_cast<uint32_t>(manufacturer_id
) << 16) |
36 (static_cast<uint32_t>(product_code
)));
41 bool GetDisplayIdFromEDID(const std::vector
<uint8_t>& edid
,
43 int64_t* display_id_out
,
44 int64_t* product_id_out
) {
45 uint16_t manufacturer_id
= 0;
46 uint16_t product_code
= 0;
47 std::string product_name
;
49 // ParseOutputDeviceData fails if it doesn't have product_name.
50 ParseOutputDeviceData(edid
, &manufacturer_id
, &product_code
, &product_name
,
53 if (manufacturer_id
!= 0) {
54 // Generates product specific value from product_name instead of product
56 // See crbug.com/240341
57 uint32_t product_code_hash
=
58 product_name
.empty() ? 0 : base::Hash(product_name
);
59 // An ID based on display's index will be assigned later if this call
61 *display_id_out
= GetID(
62 manufacturer_id
, product_code_hash
, output_index
);
63 // product_id is 64-bit signed so it can store -1 as kInvalidProductID and
64 // not match a valid product id which will all be in the lowest 32-bits.
66 *product_id_out
= GetProductID(manufacturer_id
, product_code
);
72 bool ParseOutputDeviceData(const std::vector
<uint8_t>& edid
,
73 uint16_t* manufacturer_id
,
74 uint16_t* product_code
,
75 std::string
* human_readable_name
,
76 gfx::Size
* active_pixel_out
,
77 gfx::Size
* physical_display_size_out
) {
78 // See http://en.wikipedia.org/wiki/Extended_display_identification_data
79 // for the details of EDID data format. We use the following data:
80 // bytes 8-9: manufacturer EISA ID, in big-endian
81 // bytes 10-11: manufacturer product code, in little-endian
82 // bytes 54-125: four descriptors (18-bytes each) which may contain
84 const unsigned int kManufacturerOffset
= 8;
85 const unsigned int kManufacturerLength
= 2;
86 const unsigned int kProductCodeOffset
= 10;
87 const unsigned int kProductCodeLength
= 2;
88 const unsigned int kDescriptorOffset
= 54;
89 const unsigned int kNumDescriptors
= 4;
90 const unsigned int kDescriptorLength
= 18;
91 // The specifier types.
92 const unsigned char kMonitorNameDescriptor
= 0xfc;
94 if (manufacturer_id
) {
95 if (edid
.size() < kManufacturerOffset
+ kManufacturerLength
) {
96 LOG(ERROR
) << "too short EDID data: manufacturer id";
101 *reinterpret_cast<const uint16_t*>(&edid
[kManufacturerOffset
]);
102 #if defined(ARCH_CPU_LITTLE_ENDIAN)
103 *manufacturer_id
= base::ByteSwap(*manufacturer_id
);
108 if (edid
.size() < kProductCodeOffset
+ kProductCodeLength
) {
109 LOG(ERROR
) << "too short EDID data: manufacturer product code";
114 *reinterpret_cast<const uint16_t*>(&edid
[kProductCodeOffset
]);
117 if (human_readable_name
)
118 human_readable_name
->clear();
120 for (unsigned int i
= 0; i
< kNumDescriptors
; ++i
) {
121 if (edid
.size() < kDescriptorOffset
+ (i
+ 1) * kDescriptorLength
)
124 size_t offset
= kDescriptorOffset
+ i
* kDescriptorLength
;
126 // Detailed Timing Descriptor:
127 if (edid
[offset
] != 0 && edid
[offset
+ 1] != 0) {
128 const int kMaxResolution
= 10080; // 8k display.
130 if (active_pixel_out
) {
131 const int kHorizontalPixelLsbOffset
= 2;
132 const int kHorizontalPixelMsbOffset
= 4;
133 const int kVerticalPixelLsbOffset
= 5;
134 const int kVerticalPixelMsbOffset
= 7;
136 int h_lsb
= edid
[offset
+ kHorizontalPixelLsbOffset
];
137 int h_msb
= edid
[offset
+ kHorizontalPixelMsbOffset
];
138 int h_pixel
= std::min(h_lsb
+ ((h_msb
& 0xF0) << 4), kMaxResolution
);
140 int v_lsb
= edid
[offset
+ kVerticalPixelLsbOffset
];
141 int v_msb
= edid
[offset
+ kVerticalPixelMsbOffset
];
142 int v_pixel
= std::min(v_lsb
+ ((v_msb
& 0xF0) << 4), kMaxResolution
);
144 active_pixel_out
->SetSize(h_pixel
, v_pixel
);
145 // EDID may contain multiple DTD. Use first one that
146 // contains the highest resolution.
147 active_pixel_out
= nullptr;
150 if (physical_display_size_out
) {
151 const int kHorizontalSizeLsbOffset
= 12;
152 const int kVerticalSizeLsbOffset
= 13;
153 const int kSizeMsbOffset
= 14;
155 int h_lsb
= edid
[offset
+ kHorizontalSizeLsbOffset
];
156 int v_lsb
= edid
[offset
+ kVerticalSizeLsbOffset
];
158 int msb
= edid
[offset
+ kSizeMsbOffset
];
159 int h_size
= h_lsb
+ ((msb
& 0xF0) << 4);
160 int v_size
= v_lsb
+ ((msb
& 0x0F) << 8);
161 physical_display_size_out
->SetSize(h_size
, v_size
);
162 physical_display_size_out
= nullptr;
167 // EDID Other Monitor Descriptors:
168 // If the descriptor contains the display name, it has the following
171 // byte 3: descriptor type, defined above.
172 // bytes 5-17: text data, ending with \r, padding with spaces
173 // we should check bytes 0-2 and 4, since it may have other values in
174 // case that the descriptor contains other type of data.
175 if (edid
[offset
] == 0 && edid
[offset
+ 1] == 0 && edid
[offset
+ 2] == 0 &&
176 edid
[offset
+ 3] == kMonitorNameDescriptor
&& edid
[offset
+ 4] == 0 &&
177 human_readable_name
) {
178 std::string
found_name(reinterpret_cast<const char*>(&edid
[offset
+ 5]),
179 kDescriptorLength
- 5);
180 base::TrimWhitespaceASCII(
181 found_name
, base::TRIM_TRAILING
, human_readable_name
);
186 // Verify if the |human_readable_name| consists of printable characters only.
187 // TODO(oshima|muka): Consider replacing unprintable chars with white space.
188 if (human_readable_name
) {
189 for (size_t i
= 0; i
< human_readable_name
->size(); ++i
) {
190 char c
= (*human_readable_name
)[i
];
191 if (!isascii(c
) || !isprint(c
)) {
192 human_readable_name
->clear();
193 LOG(ERROR
) << "invalid EDID: human unreadable char in name";
202 bool ParseOutputOverscanFlag(const std::vector
<uint8_t>& edid
,
204 // See http://en.wikipedia.org/wiki/Extended_display_identification_data
205 // for the extension format of EDID. Also see EIA/CEA-861 spec for
206 // the format of the extensions and how video capability is encoded.
207 // - byte 0: tag. should be 02h.
208 // - byte 1: revision. only cares revision 3 (03h).
209 // - byte 4-: data block.
210 const unsigned int kExtensionBase
= 128;
211 const unsigned int kExtensionSize
= 128;
212 const unsigned int kNumExtensionsOffset
= 126;
213 const unsigned int kDataBlockOffset
= 4;
214 const unsigned char kCEAExtensionTag
= '\x02';
215 const unsigned char kExpectedExtensionRevision
= '\x03';
216 const unsigned char kExtendedTag
= 7;
217 const unsigned char kExtendedVideoCapabilityTag
= 0;
218 const unsigned int kPTOverscan
= 4;
219 const unsigned int kITOverscan
= 2;
220 const unsigned int kCEOverscan
= 0;
222 if (edid
.size() <= kNumExtensionsOffset
)
225 unsigned char num_extensions
= edid
[kNumExtensionsOffset
];
227 for (size_t i
= 0; i
< num_extensions
; ++i
) {
228 // Skip parsing the whole extension if size is not enough.
229 if (edid
.size() < kExtensionBase
+ (i
+ 1) * kExtensionSize
)
232 size_t extension_offset
= kExtensionBase
+ i
* kExtensionSize
;
233 unsigned char tag
= edid
[extension_offset
];
234 unsigned char revision
= edid
[extension_offset
+ 1];
235 if (tag
!= kCEAExtensionTag
|| revision
!= kExpectedExtensionRevision
)
238 unsigned char timing_descriptors_start
= std::min(
239 edid
[extension_offset
+ 2], static_cast<unsigned char>(kExtensionSize
));
241 for (size_t data_offset
= extension_offset
+ kDataBlockOffset
;
242 data_offset
< extension_offset
+ timing_descriptors_start
;) {
243 // A data block is encoded as:
244 // - byte 1 high 3 bits: tag. '07' for extended tags.
245 // - byte 1 remaining bits: the length of data block.
246 // - byte 2: the extended tag. '0' for video capability.
247 // - byte 3: the capability.
248 unsigned char tag
= edid
[data_offset
] >> 5;
249 unsigned char payload_length
= edid
[data_offset
] & 0x1f;
250 if (data_offset
+ payload_length
> edid
.size())
253 if (tag
!= kExtendedTag
|| payload_length
< 2 ||
254 edid
[data_offset
+ 1] != kExtendedVideoCapabilityTag
) {
255 data_offset
+= payload_length
+ 1;
259 // The difference between preferred, IT, and CE video formats
260 // doesn't matter. Sets |flag| to true if any of these flags are true.
261 if ((edid
[data_offset
+ 2] & (1 << kPTOverscan
)) ||
262 (edid
[data_offset
+ 2] & (1 << kITOverscan
)) ||
263 (edid
[data_offset
+ 2] & (1 << kCEOverscan
))) {