Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qedidparser.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
2// Copyright (C) 2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:critical reason:data-parser
5
6#include <QtCore/QFile>
7#include <QtCore/QByteArrayView>
8
11
12#define EDID_DESCRIPTOR_ALPHANUMERIC_STRING 0xfe
13#define EDID_DESCRIPTOR_PRODUCT_NAME 0xfc
14#define EDID_DESCRIPTOR_SERIAL_NUMBER 0xff
15
16#define EDID_DATA_BLOCK_COUNT 4
17#define EDID_OFFSET_DATA_BLOCKS 0x36
18#define EDID_OFFSET_LAST_BLOCK 0x6c
19#define EDID_OFFSET_PNP_ID 0x08
20#define EDID_OFFSET_SERIAL 0x0c
21#define EDID_PHYSICAL_WIDTH 0x15
22#define EDID_OFFSET_PHYSICAL_HEIGHT 0x16
23#define EDID_TRANSFER_FUNCTION 0x17
24#define EDID_FEATURE_SUPPORT 0x18
25#define EDID_CHROMATICITIES_BLOCK 0x19
26
28
29using namespace Qt::StringLiterals;
30
31static QString lookupVendorIdInSystemDatabase(QByteArrayView id)
32{
33 QString result;
34
35 const QString fileName = "/usr/share/hwdata/pnp.ids"_L1;
36 QFile file(fileName);
37 if (!file.open(QFile::ReadOnly))
38 return result;
39
40 // On Ubuntu 20.04 the longest line in the file is 85 bytes, so this
41 // leaves plenty of room...
42 constexpr int MaxLineSize = 512;
43 char buf[MaxLineSize];
44
45 while (!file.atEnd()) {
46 auto read = file.readLine(buf, MaxLineSize);
47 if (read < 0 || read == MaxLineSize) // read error
48 break;
49
50 QByteArrayView line(buf, read - 1); // -1 to remove the trailing newline
51 if (line.isEmpty())
52 continue;
53
54 if (line.startsWith('#'))
55 continue;
56
57 auto tabPosition = line.indexOf('\t');
58 if (tabPosition <= 0) // no vendor id
59 continue;
60 if (tabPosition + 1 == line.size()) // no vendor name
61 continue;
62
63 if (line.first(tabPosition) == id) {
64 auto vendor = line.sliced(tabPosition + 1);
65 result = QString::fromUtf8(vendor.data(), vendor.size());
66 break;
67 }
68 }
69
70 return result;
71}
72
73bool QEdidParser::parse(const QByteArray &blob)
74{
75 const quint8 *data = reinterpret_cast<const quint8 *>(blob.constData());
76 const size_t length = blob.size();
77
78 // Verify header
79 if (length < 128)
80 return false;
81 if (data[0] != 0x00 || data[1] != 0xff)
82 return false;
83
84 /* Decode the PNP ID from three 5 bit words packed into 2 bytes
85 * /--08--\/--09--\
86 * 7654321076543210
87 * |\---/\---/\---/
88 * R C1 C2 C3 */
89 char pnpId[3];
90 pnpId[0] = 'A' + ((data[EDID_OFFSET_PNP_ID] & 0x7c) / 4) - 1;
91 pnpId[1] = 'A' + ((data[EDID_OFFSET_PNP_ID] & 0x3) * 8) + ((data[EDID_OFFSET_PNP_ID + 1] & 0xe0) / 32) - 1;
92 pnpId[2] = 'A' + (data[EDID_OFFSET_PNP_ID + 1] & 0x1f) - 1;
93
94 // Clear manufacturer
95 manufacturer = QString();
96
97 // Serial number, will be overwritten by an ASCII descriptor
98 // when and if it will be found
99 quint32 serial = data[EDID_OFFSET_SERIAL]
100 + (data[EDID_OFFSET_SERIAL + 1] << 8)
101 + (data[EDID_OFFSET_SERIAL + 2] << 16)
102 + (data[EDID_OFFSET_SERIAL + 3] << 24);
103 if (serial > 0)
104 serialNumber = QString::number(serial);
105 else
106 serialNumber = QString();
107
108 // Parse EDID data
109 for (int i = 0; i < EDID_DATA_BLOCK_COUNT; ++i) {
110 const uint offset = EDID_OFFSET_DATA_BLOCKS + i * 18;
111
112 if (data[offset] != 0 || data[offset + 1] != 0 || data[offset + 2] != 0)
113 continue;
114
115 if (data[offset + 3] == EDID_DESCRIPTOR_PRODUCT_NAME)
116 model = parseEdidString(&data[offset + 5]);
117 else if (data[offset + 3] == EDID_DESCRIPTOR_ALPHANUMERIC_STRING)
118 identifier = parseEdidString(&data[offset + 5]);
119 else if (data[offset + 3] == EDID_DESCRIPTOR_SERIAL_NUMBER)
120 serialNumber = parseEdidString(&data[offset + 5]);
121 }
122
123 // Try to use cache first because it is potentially more updated
124 manufacturer = lookupVendorIdInSystemDatabase(pnpId);
125
126 if (manufacturer.isEmpty()) {
127 // Find the manufacturer from the vendor lookup table
128 const auto compareVendorId = [](const QEdidVendorId &vendor, const char *str)
129 {
130 return strncmp(vendor.id, str, 3) < 0;
131 };
132
133 const auto b = std::begin(q_edidVendorIds);
134 const auto e = std::end(q_edidVendorIds);
135 auto it = std::lower_bound(b,
136 e,
137 pnpId,
138 compareVendorId);
139
140 if (it != e && strncmp(it->id, pnpId, 3) == 0)
141 manufacturer = QString::fromUtf8(q_edidVendorNames + q_edidVendorNamesOffsets[it - b]);
142 }
143
144 // If we don't know the manufacturer, fallback to PNP ID
145 if (manufacturer.isEmpty())
146 manufacturer = QString::fromUtf8(pnpId, std::size(pnpId));
147
148 // Physical size
149 physicalSize = QSizeF(data[EDID_PHYSICAL_WIDTH], data[EDID_OFFSET_PHYSICAL_HEIGHT]) * 10;
150
151 // Gamma and transfer function
152 const uint igamma = data[EDID_TRANSFER_FUNCTION];
153 if (igamma != 0xff) {
154 gamma = 1.0 + (igamma / 100.0f);
155 useTables = false;
156 } else {
157 gamma = 0.0; // Defined in DI-EXT
158 useTables = true;
159 }
160 sRgb = data[EDID_FEATURE_SUPPORT] & 0x04;
161
162 // Chromaticities
163 int rx = (data[EDID_CHROMATICITIES_BLOCK] >> 6) & 0x03;
164 int ry = (data[EDID_CHROMATICITIES_BLOCK] >> 4) & 0x03;
165 int gx = (data[EDID_CHROMATICITIES_BLOCK] >> 2) & 0x03;
166 int gy = (data[EDID_CHROMATICITIES_BLOCK] >> 0) & 0x03;
167 int bx = (data[EDID_CHROMATICITIES_BLOCK + 1] >> 6) & 0x03;
168 int by = (data[EDID_CHROMATICITIES_BLOCK + 1] >> 4) & 0x03;
169 int wx = (data[EDID_CHROMATICITIES_BLOCK + 1] >> 2) & 0x03;
170 int wy = (data[EDID_CHROMATICITIES_BLOCK + 1] >> 0) & 0x03;
171 rx |= data[EDID_CHROMATICITIES_BLOCK + 2] << 2;
172 ry |= data[EDID_CHROMATICITIES_BLOCK + 3] << 2;
173 gx |= data[EDID_CHROMATICITIES_BLOCK + 4] << 2;
174 gy |= data[EDID_CHROMATICITIES_BLOCK + 5] << 2;
175 bx |= data[EDID_CHROMATICITIES_BLOCK + 6] << 2;
176 by |= data[EDID_CHROMATICITIES_BLOCK + 7] << 2;
177 wx |= data[EDID_CHROMATICITIES_BLOCK + 8] << 2;
178 wy |= data[EDID_CHROMATICITIES_BLOCK + 9] << 2;
179
180 redChromaticity.setX(rx * (1.0f / 1024.0f));
181 redChromaticity.setY(ry * (1.0f / 1024.0f));
182 greenChromaticity.setX(gx * (1.0f / 1024.0f));
183 greenChromaticity.setY(gy * (1.0f / 1024.0f));
184 blueChromaticity.setX(bx * (1.0f / 1024.0f));
185 blueChromaticity.setY(by * (1.0f / 1024.0f));
186 whiteChromaticity.setX(wx * (1.0f / 1024.0f));
187 whiteChromaticity.setY(wy * (1.0f / 1024.0f));
188
189 // Find extensions
190 for (uint i = 1; i < length / 128; ++i) {
191 uint extensionId = data[i * 128];
192 if (extensionId == 0x40) { // DI-EXT
193 // 0x0E (sub-pixel layout)
194 // 0x20->0x22 (bits per color)
195 // 0x51->0x7e Transfer characteristics
196 const uchar desc = data[i * 128 + 0x51];
197 const uchar len = desc & 0x3f;
198 if ((desc & 0xc0) == 0x40) {
199 if (len > 45)
200 return false;
201 QList<uint16_t> whiteTRC;
202 whiteTRC.reserve(len + 1);
203 for (uint j = 0; j < len; ++j)
204 whiteTRC[j] = data[0x52 + j] * 0x101;
205 whiteTRC[len] = 0xffff;
206 tables.append(whiteTRC);
207 } else if ((desc & 0xc0) == 0x80) {
208 if (len > 15)
209 return false;
210 QList<uint16_t> redTRC;
211 QList<uint16_t> greenTRC;
212 QList<uint16_t> blueTRC;
213 blueTRC.reserve(len + 1);
214 greenTRC.reserve(len + 1);
215 redTRC.reserve(len + 1);
216 for (uint j = 0; j < len; ++j)
217 blueTRC[j] = data[0x52 + j] * 0x101;
218 blueTRC[len] = 0xffff;
219 for (uint j = 0; j < len; ++j)
220 greenTRC[j] = data[0x61 + j] * 0x101;
221 greenTRC[len] = 0xffff;
222 for (uint j = 0; j < len; ++j)
223 redTRC[j] = data[0x70 + j] * 0x101;
224 redTRC[len] = 0xffff;
225 tables.append(redTRC);
226 tables.append(greenTRC);
227 tables.append(blueTRC);
228 }
229 }
230 }
231
232 return true;
233}
234
235QString QEdidParser::parseEdidString(const quint8 *data)
236{
237 QByteArray buffer(reinterpret_cast<const char *>(data), 13);
238
239 for (int i = 0; i < buffer.size(); ++i) {
240 // If there are less than 13 characters in the string, the string
241 // is terminated with the ASCII code ‘0Ah’ (line feed) and padded
242 // with ASCII code ‘20h’ (space). See EDID 1.4, sections 3.10.3.1,
243 // 3.10.3.2, and 3.10.3.4.
244 if (buffer[i] == '\n') {
245 buffer.truncate(i);
246 break;
247 }
248
249 // Replace non-printable characters with dash
250 if (buffer[i] < '\040' || buffer[i] > '\176')
251 buffer[i] = '-';
252 }
253
254 return QString::fromLatin1(buffer);
255}
256
257QT_END_NAMESPACE
#define EDID_DESCRIPTOR_SERIAL_NUMBER
#define EDID_FEATURE_SUPPORT
#define EDID_CHROMATICITIES_BLOCK
#define EDID_TRANSFER_FUNCTION
static QString lookupVendorIdInSystemDatabase(QByteArrayView id)
#define EDID_OFFSET_SERIAL
#define EDID_DESCRIPTOR_ALPHANUMERIC_STRING
#define EDID_OFFSET_PNP_ID
#define EDID_OFFSET_PHYSICAL_HEIGHT
#define EDID_DESCRIPTOR_PRODUCT_NAME
#define EDID_PHYSICAL_WIDTH
#define EDID_DATA_BLOCK_COUNT
#define EDID_OFFSET_DATA_BLOCKS