1 #include <assert.h>
2 #include <errno.h>
3 #include <inttypes.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <sys/types.h>
7
8 #include "bits.h"
9 #include "displayid.h"
10
11 /**
12 * The size of the mandatory fields in a DisplayID section.
13 */
14 #define DISPLAYID_MIN_SIZE 5
15 /**
16 * The maximum size of a DisplayID section.
17 */
18 #define DISPLAYID_MAX_SIZE 256
19 /**
20 * The size of a DisplayID data block header (tag, revision and size).
21 */
22 #define DISPLAYID_DATA_BLOCK_HEADER_SIZE 3
23 /**
24 * The size of a DisplayID type I timing.
25 */
26 #define DISPLAYID_TYPE_I_TIMING_SIZE 20
27 /**
28 * The size of a DisplayID type II timing.
29 */
30 #define DISPLAYID_TYPE_II_TIMING_SIZE 11
31 /**
32 * The size of a DisplayID type III timing.
33 */
34 #define DISPLAYID_TYPE_III_TIMING_SIZE 3
35
36 static bool
is_all_zeroes(const uint8_t * data,size_t size)37 is_all_zeroes(const uint8_t *data, size_t size)
38 {
39 size_t i;
40
41 for (i = 0; i < size; i++) {
42 if (data[i] != 0)
43 return false;
44 }
45
46 return true;
47 }
48
49 static void
add_failure(struct di_displayid * displayid,const char fmt[],...)50 add_failure(struct di_displayid *displayid, const char fmt[], ...)
51 {
52 va_list args;
53
54 va_start(args, fmt);
55 _di_logger_va_add_failure(displayid->logger, fmt, args);
56 va_end(args);
57 }
58
59 static void
logger_add_failure(struct di_logger * logger,const char fmt[],...)60 logger_add_failure(struct di_logger *logger, const char fmt[], ...)
61 {
62 va_list args;
63
64 va_start(args, fmt);
65 _di_logger_va_add_failure(logger, fmt, args);
66 va_end(args);
67 }
68
69 static void
check_data_block_revision(struct di_displayid * displayid,const uint8_t data[static DISPLAYID_DATA_BLOCK_HEADER_SIZE],const char * block_name,uint8_t max_revision)70 check_data_block_revision(struct di_displayid *displayid,
71 const uint8_t data[static DISPLAYID_DATA_BLOCK_HEADER_SIZE],
72 const char *block_name, uint8_t max_revision)
73 {
74 uint8_t revision, flags;
75
76 flags = get_bit_range(data[0x01], 7, 3);
77 revision = get_bit_range(data[0x01], 2, 0);
78
79 if (revision > max_revision) {
80 add_failure(displayid, "%s: Unexpected revision (%u != %u).",
81 block_name, revision, max_revision);
82 }
83 if (flags != 0) {
84 add_failure(displayid, "%s: Unexpected flags (0x%02x).",
85 block_name, flags);
86 }
87 }
88
89 static bool
parse_display_params_block(struct di_displayid * displayid,struct di_displayid_display_params_priv * priv,const uint8_t * data,size_t size)90 parse_display_params_block(struct di_displayid *displayid,
91 struct di_displayid_display_params_priv *priv,
92 const uint8_t *data, size_t size)
93 {
94 struct di_displayid_display_params *params = &priv->base;
95 uint8_t raw_features;
96
97 check_data_block_revision(displayid, data,
98 "Display Parameters Data Block",
99 0);
100
101 if (size != 0x0F) {
102 add_failure(displayid, "Display Parameters Data Block: DisplayID payload length is different than expected (%zu != %zu)", size, 0x0F);
103 return false;
104 }
105
106 params->horiz_image_mm = 0.1f * (float)(data[0x03] | (data[0x04] << 8));
107 params->vert_image_mm = 0.1f * (float)(data[0x05] | (data[0x06] << 8));
108
109 params->horiz_pixels = data[0x07] | (data[0x08] << 8);
110 params->vert_pixels = data[0x09] | (data[0x0A] << 8);
111
112 raw_features = data[0x0B];
113 params->features = &priv->features;
114 priv->features.audio = has_bit(raw_features, 7);
115 priv->features.separate_audio_inputs = has_bit(raw_features, 6);
116 priv->features.audio_input_override = has_bit(raw_features, 5);
117 priv->features.power_management = has_bit(raw_features, 4);
118 priv->features.fixed_timing = has_bit(raw_features, 3);
119 priv->features.fixed_pixel_format = has_bit(raw_features, 2);
120 priv->features.ai = has_bit(raw_features, 1);
121 priv->features.deinterlacing = has_bit(raw_features, 0);
122
123 if (data[0x0C] != 0xFF)
124 params->gamma = (float)data[0x0C] / 100 + 1;
125 params->aspect_ratio = (float)data[0x0D] / 100 + 1;
126 params->bits_per_color_overall = get_bit_range(data[0x0E], 7, 4) + 1;
127 params->bits_per_color_native = get_bit_range(data[0x0E], 3, 0) + 1;
128
129 return true;
130 }
131
132 static bool
timing_aspect_ratio_is_valid(uint8_t raw)133 timing_aspect_ratio_is_valid(uint8_t raw)
134 {
135 switch (raw) {
136 case DI_DISPLAYID_TIMING_ASPECT_RATIO_1_1:
137 case DI_DISPLAYID_TIMING_ASPECT_RATIO_5_4:
138 case DI_DISPLAYID_TIMING_ASPECT_RATIO_4_3:
139 case DI_DISPLAYID_TIMING_ASPECT_RATIO_15_9:
140 case DI_DISPLAYID_TIMING_ASPECT_RATIO_16_9:
141 case DI_DISPLAYID_TIMING_ASPECT_RATIO_16_10:
142 case DI_DISPLAYID_TIMING_ASPECT_RATIO_64_27:
143 case DI_DISPLAYID_TIMING_ASPECT_RATIO_256_135:
144 case DI_DISPLAYID_TIMING_ASPECT_RATIO_UNDEFINED:
145 return true;
146 default:
147 return false;
148 }
149 }
150
151 bool
_di_displayid_parse_type_1_7_timing(struct di_displayid_type_i_ii_vii_timing * t,struct di_logger * logger,const char * prefix,const uint8_t * data,bool is_type7)152 _di_displayid_parse_type_1_7_timing(struct di_displayid_type_i_ii_vii_timing *t,
153 struct di_logger *logger,
154 const char *prefix,
155 const uint8_t *data,
156 bool is_type7)
157 {
158 int raw_pixel_clock;
159 uint8_t stereo_3d, aspect_ratio;
160
161 raw_pixel_clock = data[0] | (data[1] << 8) | (data[2] << 16);
162 if (is_type7)
163 t->pixel_clock_mhz = (double)(1 + raw_pixel_clock) * 0.001;
164 else
165 t->pixel_clock_mhz = (double)(1 + raw_pixel_clock) * 0.01;
166
167 t->preferred = has_bit(data[3], 7);
168 t->interlaced = has_bit(data[3], 4);
169
170 stereo_3d = get_bit_range(data[3], 6, 5);
171 switch (stereo_3d) {
172 case DI_DISPLAYID_TYPE_I_II_VII_TIMING_STEREO_3D_NEVER:
173 case DI_DISPLAYID_TYPE_I_II_VII_TIMING_STEREO_3D_ALWAYS:
174 case DI_DISPLAYID_TYPE_I_II_VII_TIMING_STEREO_3D_USER:
175 t->stereo_3d = stereo_3d;
176 break;
177 default:
178 logger_add_failure(logger, "%s: Reserved stereo 0x%02x.",
179 prefix, stereo_3d);
180 break;
181 }
182
183 aspect_ratio = get_bit_range(data[3], 3, 0);
184 if (timing_aspect_ratio_is_valid(aspect_ratio)) {
185 t->aspect_ratio = aspect_ratio;
186 } else {
187 t->aspect_ratio = DI_DISPLAYID_TIMING_ASPECT_RATIO_UNDEFINED;
188 logger_add_failure(logger, "%s: Unknown aspect 0x%02x.",
189 prefix, aspect_ratio);
190 }
191
192 t->horiz_active = 1 + (data[4] | (data[5] << 8));
193 t->horiz_blank = 1 + (data[6] | (data[7] << 8));
194 t->horiz_offset = 1 + (data[8] | (get_bit_range(data[9], 6, 0) << 8));
195 t->horiz_sync_polarity = has_bit(data[9], 7);
196 t->horiz_sync_width = 1 + (data[10] | (data[11] << 8));
197 t->vert_active = 1 + (data[12] | (data[13] << 8));
198 t->vert_blank = 1 + (data[14] | (data[15] << 8));
199 t->vert_offset = 1 + (data[16] | (get_bit_range(data[17], 6, 0) << 8));
200 t->vert_sync_polarity = has_bit(data[17], 7);
201 t->vert_sync_width = 1 + (data[18] | (data[19] << 8));
202
203 return true;
204 }
205
206 static bool
parse_type_i_timing(struct di_displayid * displayid,struct di_displayid_data_block * data_block,const uint8_t data[static DISPLAYID_TYPE_I_TIMING_SIZE])207 parse_type_i_timing(struct di_displayid *displayid,
208 struct di_displayid_data_block *data_block,
209 const uint8_t data[static DISPLAYID_TYPE_I_TIMING_SIZE])
210 {
211 struct di_displayid_type_i_ii_vii_timing timing, *t;
212
213 if (!_di_displayid_parse_type_1_7_timing(&timing, displayid->logger,
214 "Video Timing Modes Type 1 - Detailed Timings Data Block",
215 data, false))
216 return false;
217
218 t = calloc(1, sizeof(*t));
219 if (t == NULL) {
220 return false;
221 }
222
223 *t = timing;
224 assert(data_block->type_i_timings_len < DISPLAYID_MAX_TYPE_I_TIMINGS);
225 data_block->type_i_timings[data_block->type_i_timings_len++] = t;
226 return true;
227 }
228
229 static bool
parse_type_i_timing_block(struct di_displayid * displayid,struct di_displayid_data_block * data_block,const uint8_t * data,size_t size)230 parse_type_i_timing_block(struct di_displayid *displayid,
231 struct di_displayid_data_block *data_block,
232 const uint8_t *data, size_t size)
233 {
234 size_t i;
235
236 check_data_block_revision(displayid, data,
237 "Video Timing Modes Type 1 - Detailed Timings Data Block",
238 1);
239
240 if ((size - DISPLAYID_DATA_BLOCK_HEADER_SIZE) % DISPLAYID_TYPE_I_TIMING_SIZE != 0) {
241 add_failure(displayid,
242 "Video Timing Modes Type 1 - Detailed Timings Data Block: payload size not divisible by element size.");
243 }
244
245 for (i = DISPLAYID_DATA_BLOCK_HEADER_SIZE;
246 i + DISPLAYID_TYPE_I_TIMING_SIZE <= size;
247 i += DISPLAYID_TYPE_I_TIMING_SIZE) {
248 if (!parse_type_i_timing(displayid, data_block, &data[i])) {
249 return false;
250 }
251 }
252
253 return true;
254 }
255
256 static bool
parse_type_ii_timing(struct di_displayid * displayid,struct di_displayid_data_block * data_block,const uint8_t data[static DISPLAYID_TYPE_II_TIMING_SIZE])257 parse_type_ii_timing(struct di_displayid *displayid,
258 struct di_displayid_data_block *data_block,
259 const uint8_t data[static DISPLAYID_TYPE_II_TIMING_SIZE])
260 {
261 int raw_pixel_clock;
262 uint8_t stereo_3d;
263
264 struct di_displayid_type_i_ii_vii_timing *t = calloc(1, sizeof(*t));
265 if (t == NULL) {
266 return false;
267 }
268
269 t->aspect_ratio = DI_DISPLAYID_TIMING_ASPECT_RATIO_UNDEFINED;
270
271 raw_pixel_clock = data[0] | (data[1] << 8) | (data[2] << 16);
272 t->pixel_clock_mhz = (double)(1 + raw_pixel_clock) * 0.01;
273
274 t->preferred = has_bit(data[3], 7);
275 t->interlaced = has_bit(data[3], 4);
276
277 stereo_3d = get_bit_range(data[3], 6, 5);
278 switch (stereo_3d) {
279 case DI_DISPLAYID_TYPE_I_II_VII_TIMING_STEREO_3D_NEVER:
280 case DI_DISPLAYID_TYPE_I_II_VII_TIMING_STEREO_3D_ALWAYS:
281 case DI_DISPLAYID_TYPE_I_II_VII_TIMING_STEREO_3D_USER:
282 t->stereo_3d = stereo_3d;
283 break;
284 default:
285 add_failure(displayid,
286 "Video Timing Modes Type 2 - Detailed Timings Data Block: Reserved stereo 0x%02x.",
287 stereo_3d);
288 break;
289 }
290
291 t->horiz_sync_polarity = has_bit(data[3], 3);
292 t->vert_sync_polarity = has_bit(data[3], 2);
293
294 if (get_bit_range(data[3], 1, 0) != 0) {
295 add_failure(displayid,
296 "Video Timing Modes Type 2 - Detailed Timings Data Block: "
297 "Timing Options bit 1-0 are reserved.");
298 }
299
300 t->horiz_active = 8 + 8 * (data[4] | (get_bit_range(data[5], 0, 0) << 8));
301 t->horiz_blank = 8 + 8 * get_bit_range(data[5], 7, 1);
302 t->horiz_offset = 8 + 8 * get_bit_range(data[6], 7, 4);
303 t->horiz_sync_width = 8 + 8 * get_bit_range(data[6], 3, 0);
304 t->vert_active = 1 + (data[7] | (get_bit_range(data[8], 3, 0) << 8));
305 if (get_bit_range(data[8], 7, 4) != 0) {
306 add_failure(displayid,
307 "Video Timing Modes Type 2 - Detailed Timings Data Block: "
308 "Vertical Active Image bits 7-4 are reserved.");
309 }
310 t->vert_blank = 1 + data[9];
311 t->vert_offset = 1 + get_bit_range(data[9], 7, 4);
312 t->vert_sync_width = 1 + get_bit_range(data[9], 3, 0);
313
314 assert(data_block->type_ii_timings_len < DISPLAYID_MAX_TYPE_II_TIMINGS);
315 data_block->type_ii_timings[data_block->type_ii_timings_len++] = t;
316 return true;
317 }
318
319 static bool
parse_type_ii_timing_block(struct di_displayid * displayid,struct di_displayid_data_block * data_block,const uint8_t * data,size_t size)320 parse_type_ii_timing_block(struct di_displayid *displayid,
321 struct di_displayid_data_block *data_block,
322 const uint8_t *data, size_t size)
323 {
324 size_t i;
325
326 check_data_block_revision(displayid, data,
327 "Video Timing Modes Type 2 - Detailed Timings Data Block",
328 0);
329
330 if ((size - DISPLAYID_DATA_BLOCK_HEADER_SIZE) % DISPLAYID_TYPE_II_TIMING_SIZE != 0) {
331 add_failure(displayid,
332 "Video Timing Modes Type 2 - Detailed Timings Data Block: payload size not divisible by element size.");
333 }
334
335 for (i = DISPLAYID_DATA_BLOCK_HEADER_SIZE;
336 i + DISPLAYID_TYPE_II_TIMING_SIZE <= size;
337 i += DISPLAYID_TYPE_II_TIMING_SIZE) {
338 if (!parse_type_ii_timing(displayid, data_block, &data[i])) {
339 return false;
340 }
341 }
342
343 return true;
344 }
345
346 static bool
parse_tiled_topo_block(struct di_displayid * displayid,struct di_displayid_tiled_topo_priv * priv,const uint8_t * data,size_t size)347 parse_tiled_topo_block(struct di_displayid *displayid,
348 struct di_displayid_tiled_topo_priv *priv,
349 const uint8_t *data, size_t size)
350 {
351 struct di_displayid_tiled_topo *tiled_topo = &priv->base;
352 uint8_t raw_caps, raw_missing_recv_behavior, raw_single_recv_behavior;
353 bool has_bezel;
354 float px_mult;
355
356 check_data_block_revision(displayid, data,
357 "Tiled Display Topology Data Block",
358 0);
359
360 if (size - DISPLAYID_DATA_BLOCK_HEADER_SIZE != 22) {
361 add_failure(displayid,
362 "Tiled Display Topology Data Block: DisplayID payload length is different than expected (%zu != %zu)",
363 size - DISPLAYID_DATA_BLOCK_HEADER_SIZE, 22);
364 return false;
365 }
366
367 raw_caps = data[0x03];
368 tiled_topo->caps = &priv->caps;
369 priv->caps.single_enclosure = has_bit(raw_caps, 7);
370 has_bezel = has_bit(raw_caps, 6);
371 raw_missing_recv_behavior = get_bit_range(raw_caps, 4, 3);
372 raw_single_recv_behavior = get_bit_range(raw_caps, 2, 0);
373 if (has_bit(raw_caps, 5)) {
374 add_failure(displayid, "Tiled Display Topology Data Block: Capability bit 5 is reserved.");
375 }
376
377 switch (raw_missing_recv_behavior) {
378 case DI_DISPLAYID_TILED_TOPO_MISSING_RECV_UNDEF:
379 case DI_DISPLAYID_TILED_TOPO_MISSING_RECV_TILE_ONLY:
380 priv->caps.missing_recv_behavior = raw_missing_recv_behavior;
381 break;
382 default:
383 add_failure(displayid,
384 "Tiled Display Topology Data Block: Behavior if more than one tile and fewer than total number of tiles set to reserved value 0x%02x.",
385 raw_missing_recv_behavior);
386 break;
387 }
388
389 switch (raw_single_recv_behavior) {
390 case DI_DISPLAYID_TILED_TOPO_SINGLE_RECV_UNDEF:
391 case DI_DISPLAYID_TILED_TOPO_SINGLE_RECV_TILE_ONLY:
392 case DI_DISPLAYID_TILED_TOPO_SINGLE_RECV_SCALED:
393 case DI_DISPLAYID_TILED_TOPO_SINGLE_RECV_CLONED:
394 priv->caps.single_recv_behavior = raw_single_recv_behavior;
395 break;
396 default:
397 add_failure(displayid,
398 "Tiled Display Topology Data Block: Behavior if it is the only tile set to reserved value 0x%02x.",
399 raw_single_recv_behavior);
400 break;
401 }
402
403 tiled_topo->total_horiz_tiles = 1 + (get_bit_range(data[0x04], 7, 4) |
404 (get_bit_range(data[0x06], 7, 6) << 4));
405 tiled_topo->total_vert_tiles = 1 + (get_bit_range(data[0x04], 3, 0) |
406 (get_bit_range(data[0x06], 5, 4) << 4));
407 tiled_topo->horiz_tile_location = 1 + (get_bit_range(data[0x05], 7, 4) |
408 (get_bit_range(data[0x06], 3, 2) << 4));
409 tiled_topo->vert_tile_location = 1 + (get_bit_range(data[0x05], 3, 0) |
410 (get_bit_range(data[0x06], 1, 0) << 4));
411
412 tiled_topo->horiz_tile_pixels = 1 + (data[0x07] | (data[0x08] << 8));
413 tiled_topo->vert_tile_lines = 1 + (data[0x09] | (data[0x0A] << 8));
414
415 px_mult = data[0x0B];
416 if (has_bezel && px_mult == 0) {
417 add_failure(displayid, "Tiled Display Topology Data Block: Bezel information bit is set, but the pixel multiplier is zero.");
418 has_bezel = false;
419 }
420 if (has_bezel) {
421 tiled_topo->bezel = &priv->bezel;
422 priv->bezel.top_px = px_mult * data[0x0C] * 0.1f;
423 priv->bezel.bottom_px = px_mult * data[0x0D] * 0.1f;
424 priv->bezel.right_px = px_mult * data[0x0E] * 0.1f;
425 priv->bezel.left_px = px_mult * data[0x0F] * 0.1f;
426 } else {
427 if (px_mult != 0)
428 add_failure(displayid, "Tiled Display Topology Data Block: No bezel information, but the pixel multiplier is non-zero.");
429 if (!is_all_zeroes(&data[0x0C], 4))
430 add_failure(displayid, "Tiled Display Topology Data Block: No bezel information, but the bezel size is non-zero.");
431 }
432
433 memcpy(tiled_topo->vendor_id, &data[0x10], 3);
434 tiled_topo->product_code = (uint16_t)(data[0x13] | (data[0x14] << 8));
435 tiled_topo->serial_number = data[0x15] |
436 (uint32_t)(data[0x16] << 8) |
437 (uint32_t)(data[0x17] << 16) |
438 (uint32_t)(data[0x18] << 24);
439
440 return true;
441 }
442
443 static bool
parse_type_iii_timing(struct di_displayid * displayid,struct di_displayid_data_block * data_block,const uint8_t data[static DISPLAYID_TYPE_III_TIMING_SIZE])444 parse_type_iii_timing(struct di_displayid *displayid,
445 struct di_displayid_data_block *data_block,
446 const uint8_t data[static DISPLAYID_TYPE_III_TIMING_SIZE])
447 {
448 struct di_displayid_type_iii_timing *t;
449 uint8_t algo, aspect_ratio;
450
451 t = calloc(1, sizeof(*t));
452 if (t == NULL)
453 return false;
454
455 t->preferred = has_bit(data[0], 7);
456
457 algo = get_bit_range(data[0], 6, 4);
458 switch (algo) {
459 case DI_DISPLAYID_TYPE_III_TIMING_CVT_STANDARD_BLANKING:
460 case DI_DISPLAYID_TYPE_III_TIMING_CVT_REDUCED_BLANKING:
461 t->algo = algo;
462 break;
463 default:
464 add_failure(displayid,
465 "Video Timing Modes Type 3 - Short Timings Data Block: Reserved algorithm 0x%02x.",
466 algo);
467 goto error_reserved;
468 }
469
470 aspect_ratio = get_bit_range(data[0], 3, 0);
471 if (timing_aspect_ratio_is_valid(aspect_ratio)) {
472 t->aspect_ratio = aspect_ratio;
473 } else {
474 add_failure(displayid,
475 "Video Timing Modes Type 3 - Short Timings Data Block: Reserved aspect ratio 0x%02x.",
476 aspect_ratio);
477 goto error_reserved;
478 }
479
480 t->horiz_active = ((int32_t)data[1] + 1) * 8;
481
482 t->interlaced = has_bit(data[2], 7);
483 t->refresh_rate_hz = (int32_t)get_bit_range(data[2], 6, 0) + 1;
484
485 assert(data_block->type_iii_timings_len < DISPLAYID_MAX_TYPE_III_TIMINGS);
486 data_block->type_iii_timings[data_block->type_iii_timings_len++] = t;
487 return true;
488
489 error_reserved:
490 free(t);
491 return true;
492 }
493
494 static bool
parse_type_iii_timing_block(struct di_displayid * displayid,struct di_displayid_data_block * data_block,const uint8_t * data,size_t size)495 parse_type_iii_timing_block(struct di_displayid *displayid,
496 struct di_displayid_data_block *data_block,
497 const uint8_t *data, size_t size)
498 {
499 size_t i;
500
501 check_data_block_revision(displayid, data,
502 "Video Timing Modes Type 3 - Short Timings Data Block",
503 1);
504
505 if ((size - DISPLAYID_DATA_BLOCK_HEADER_SIZE) % DISPLAYID_TYPE_III_TIMING_SIZE != 0)
506 add_failure(displayid,
507 "Video Timing Modes Type 3 - Short Timings Data Block: payload size not divisible by element size.");
508
509 for (i = DISPLAYID_DATA_BLOCK_HEADER_SIZE;
510 i + DISPLAYID_TYPE_III_TIMING_SIZE <= size;
511 i += DISPLAYID_TYPE_III_TIMING_SIZE) {
512 if (!parse_type_iii_timing(displayid, data_block, &data[i]))
513 return false;
514 }
515
516 return true;
517 }
518
519 static ssize_t
parse_data_block(struct di_displayid * displayid,const uint8_t * data,size_t size)520 parse_data_block(struct di_displayid *displayid, const uint8_t *data,
521 size_t size)
522 {
523 uint8_t tag;
524 size_t data_block_size;
525 struct di_displayid_data_block *data_block = NULL;
526
527 assert(size >= DISPLAYID_DATA_BLOCK_HEADER_SIZE);
528
529 tag = data[0x00];
530 data_block_size = (size_t) data[0x02] + DISPLAYID_DATA_BLOCK_HEADER_SIZE;
531 if (data_block_size > size) {
532 add_failure(displayid,
533 "The length of this DisplayID data block (%d) exceeds the number of bytes remaining (%zu)",
534 data_block_size, size);
535 goto skip;
536 }
537
538 data_block = calloc(1, sizeof(*data_block));
539 if (!data_block)
540 goto error;
541
542 switch (tag) {
543 case DI_DISPLAYID_DATA_BLOCK_DISPLAY_PARAMS:
544 if (!parse_display_params_block(displayid,
545 &data_block->display_params,
546 data, data_block_size))
547 goto error;
548 break;
549 case DI_DISPLAYID_DATA_BLOCK_TYPE_I_TIMING:
550 if (!parse_type_i_timing_block(displayid, data_block, data, data_block_size))
551 goto error;
552 break;
553 case DI_DISPLAYID_DATA_BLOCK_TILED_DISPLAY_TOPO:
554 if (!parse_tiled_topo_block(displayid, &data_block->tiled_topo, data,
555 data_block_size))
556 goto skip;
557 break;
558 case DI_DISPLAYID_DATA_BLOCK_TYPE_II_TIMING:
559 if (!parse_type_ii_timing_block(displayid, data_block, data, data_block_size))
560 goto error;
561 break;
562 case DI_DISPLAYID_DATA_BLOCK_TYPE_III_TIMING:
563 if (!parse_type_iii_timing_block(displayid, data_block, data, data_block_size))
564 goto error;
565 break;
566 case DI_DISPLAYID_DATA_BLOCK_PRODUCT_ID:
567 case DI_DISPLAYID_DATA_BLOCK_COLOR_CHARACT:
568 case DI_DISPLAYID_DATA_BLOCK_TYPE_IV_TIMING:
569 case DI_DISPLAYID_DATA_BLOCK_VESA_TIMING:
570 case DI_DISPLAYID_DATA_BLOCK_CEA_TIMING:
571 case DI_DISPLAYID_DATA_BLOCK_TIMING_RANGE_LIMITS:
572 case DI_DISPLAYID_DATA_BLOCK_PRODUCT_SERIAL:
573 case DI_DISPLAYID_DATA_BLOCK_ASCII_STRING:
574 case DI_DISPLAYID_DATA_BLOCK_DISPLAY_DEVICE_DATA:
575 case DI_DISPLAYID_DATA_BLOCK_INTERFACE_POWER_SEQ:
576 case DI_DISPLAYID_DATA_BLOCK_TRANSFER_CHARACT:
577 case DI_DISPLAYID_DATA_BLOCK_DISPLAY_INTERFACE:
578 case DI_DISPLAYID_DATA_BLOCK_STEREO_DISPLAY_INTERFACE:
579 case DI_DISPLAYID_DATA_BLOCK_TYPE_V_TIMING:
580 case DI_DISPLAYID_DATA_BLOCK_TYPE_VI_TIMING:
581 break; /* Supported */
582 case 0x7F:
583 goto skip; /* Vendor-specific */
584 default:
585 add_failure(displayid,
586 "Unknown DisplayID Data Block (0x%" PRIx8 ", length %" PRIu8 ")",
587 tag, data_block_size - DISPLAYID_DATA_BLOCK_HEADER_SIZE);
588 goto skip;
589 }
590
591 data_block->tag = tag;
592
593 assert(displayid->data_blocks_len < DISPLAYID_MAX_DATA_BLOCKS);
594 displayid->data_blocks[displayid->data_blocks_len++] = data_block;
595 return (ssize_t) data_block_size;
596
597 skip:
598 free(data_block);
599 return (ssize_t) data_block_size;
600
601 error:
602 free(data_block);
603 return -1;
604 }
605
606 static bool
is_data_block_end(const uint8_t * data,size_t size)607 is_data_block_end(const uint8_t *data, size_t size)
608 {
609 if (size < DISPLAYID_DATA_BLOCK_HEADER_SIZE)
610 return true;
611 return is_all_zeroes(data, DISPLAYID_DATA_BLOCK_HEADER_SIZE);
612 }
613
614 static bool
validate_checksum(const uint8_t * data,size_t size)615 validate_checksum(const uint8_t *data, size_t size)
616 {
617 uint8_t sum = 0;
618 size_t i;
619
620 for (i = 0; i < size; i++) {
621 sum += data[i];
622 }
623
624 return sum == 0;
625 }
626
627 bool
_di_displayid_parse(struct di_displayid * displayid,const uint8_t * data,size_t size,struct di_logger * logger)628 _di_displayid_parse(struct di_displayid *displayid, const uint8_t *data,
629 size_t size, struct di_logger *logger)
630 {
631 size_t section_size, i, max_data_block_size;
632 ssize_t data_block_size;
633 uint8_t product_type;
634
635 if (size < DISPLAYID_MIN_SIZE) {
636 errno = EINVAL;
637 return false;
638 }
639
640 displayid->logger = logger;
641
642 displayid->version = get_bit_range(data[0x00], 7, 4);
643 displayid->revision = get_bit_range(data[0x00], 3, 0);
644 if (displayid->version == 0 || displayid->version > 1) {
645 errno = ENOTSUP;
646 return false;
647 }
648
649 section_size = (size_t) data[0x01] + DISPLAYID_MIN_SIZE;
650 if (section_size > DISPLAYID_MAX_SIZE || section_size > size) {
651 errno = EINVAL;
652 return false;
653 }
654
655 if (!validate_checksum(data, section_size)) {
656 errno = EINVAL;
657 return false;
658 }
659
660 product_type = data[0x02];
661 switch (product_type) {
662 case DI_DISPLAYID_PRODUCT_TYPE_EXTENSION:
663 case DI_DISPLAYID_PRODUCT_TYPE_TEST:
664 case DI_DISPLAYID_PRODUCT_TYPE_DISPLAY_PANEL:
665 case DI_DISPLAYID_PRODUCT_TYPE_STANDALONE_DISPLAY:
666 case DI_DISPLAYID_PRODUCT_TYPE_TV_RECEIVER:
667 case DI_DISPLAYID_PRODUCT_TYPE_REPEATER:
668 case DI_DISPLAYID_PRODUCT_TYPE_DIRECT_DRIVE:
669 displayid->product_type = product_type;
670 break;
671 default:
672 errno = EINVAL;
673 return false;
674 }
675
676 i = DISPLAYID_MIN_SIZE - 1;
677 max_data_block_size = 0;
678 while (i < section_size - 1) {
679 max_data_block_size = section_size - 1 - i;
680 if (is_data_block_end(&data[i], max_data_block_size))
681 break;
682 data_block_size = parse_data_block(displayid, &data[i],
683 max_data_block_size);
684 if (data_block_size < 0)
685 return false;
686 assert(data_block_size > 0);
687 i += (size_t) data_block_size;
688 }
689 if (!is_all_zeroes(&data[i], max_data_block_size)) {
690 if (max_data_block_size < DISPLAYID_DATA_BLOCK_HEADER_SIZE)
691 add_failure(displayid,
692 "Not enough bytes remain (%zu) for a DisplayID data block and the DisplayID filler is non-0.",
693 max_data_block_size);
694 else
695 add_failure(displayid, "Padding: Contains non-zero bytes.");
696 }
697
698 displayid->logger = NULL;
699 return true;
700 }
701
702 static void
destroy_data_block(struct di_displayid_data_block * data_block)703 destroy_data_block(struct di_displayid_data_block *data_block)
704 {
705 size_t i;
706
707 switch (data_block->tag) {
708 case DI_DISPLAYID_DATA_BLOCK_TYPE_I_TIMING:
709 for (i = 0; i < data_block->type_i_timings_len; i++)
710 free(data_block->type_i_timings[i]);
711 break;
712 case DI_DISPLAYID_DATA_BLOCK_TYPE_II_TIMING:
713 for (i = 0; i < data_block->type_ii_timings_len; i++)
714 free(data_block->type_ii_timings[i]);
715 break;
716 case DI_DISPLAYID_DATA_BLOCK_TYPE_III_TIMING:
717 for (i = 0; i < data_block->type_iii_timings_len; i++)
718 free(data_block->type_iii_timings[i]);
719 break;
720 default:
721 break; /* Nothing to do */
722 }
723
724 free(data_block);
725 }
726
727 void
_di_displayid_finish(struct di_displayid * displayid)728 _di_displayid_finish(struct di_displayid *displayid)
729 {
730 size_t i;
731
732 for (i = 0; i < displayid->data_blocks_len; i++)
733 destroy_data_block(displayid->data_blocks[i]);
734 }
735
736 int
di_displayid_get_version(const struct di_displayid * displayid)737 di_displayid_get_version(const struct di_displayid *displayid)
738 {
739 return displayid->version;
740 }
741
742 int
di_displayid_get_revision(const struct di_displayid * displayid)743 di_displayid_get_revision(const struct di_displayid *displayid)
744 {
745 return displayid->revision;
746 }
747
748 enum di_displayid_product_type
di_displayid_get_product_type(const struct di_displayid * displayid)749 di_displayid_get_product_type(const struct di_displayid *displayid)
750 {
751 return displayid->product_type;
752 }
753
754 enum di_displayid_data_block_tag
di_displayid_data_block_get_tag(const struct di_displayid_data_block * data_block)755 di_displayid_data_block_get_tag(const struct di_displayid_data_block *data_block)
756 {
757 return data_block->tag;
758 }
759
760 const struct di_displayid_display_params *
di_displayid_data_block_get_display_params(const struct di_displayid_data_block * data_block)761 di_displayid_data_block_get_display_params(const struct di_displayid_data_block *data_block)
762 {
763 if (data_block->tag != DI_DISPLAYID_DATA_BLOCK_DISPLAY_PARAMS) {
764 return NULL;
765 }
766 return &data_block->display_params.base;
767 }
768
769 const struct di_displayid_type_i_ii_vii_timing *const *
di_displayid_data_block_get_type_i_timings(const struct di_displayid_data_block * data_block)770 di_displayid_data_block_get_type_i_timings(const struct di_displayid_data_block *data_block)
771 {
772 if (data_block->tag != DI_DISPLAYID_DATA_BLOCK_TYPE_I_TIMING) {
773 return NULL;
774 }
775 return (const struct di_displayid_type_i_ii_vii_timing *const *) data_block->type_i_timings;
776 }
777
778 const struct di_displayid_type_i_ii_vii_timing *const *
di_displayid_data_block_get_type_ii_timings(const struct di_displayid_data_block * data_block)779 di_displayid_data_block_get_type_ii_timings(const struct di_displayid_data_block *data_block)
780 {
781 if (data_block->tag != DI_DISPLAYID_DATA_BLOCK_TYPE_II_TIMING) {
782 return NULL;
783 }
784 return (const struct di_displayid_type_i_ii_vii_timing *const *) data_block->type_ii_timings;
785 }
786
787 const struct di_displayid_tiled_topo *
di_displayid_data_block_get_tiled_topo(const struct di_displayid_data_block * data_block)788 di_displayid_data_block_get_tiled_topo(const struct di_displayid_data_block *data_block)
789 {
790 if (data_block->tag != DI_DISPLAYID_DATA_BLOCK_TILED_DISPLAY_TOPO) {
791 return NULL;
792 }
793 return &data_block->tiled_topo.base;
794 }
795
796 const struct di_displayid_type_iii_timing *const *
di_displayid_data_block_get_type_iii_timings(const struct di_displayid_data_block * data_block)797 di_displayid_data_block_get_type_iii_timings(const struct di_displayid_data_block *data_block)
798 {
799 if (data_block->tag != DI_DISPLAYID_DATA_BLOCK_TYPE_III_TIMING) {
800 return NULL;
801 }
802 return (const struct di_displayid_type_iii_timing *const *) data_block->type_iii_timings;
803 }
804
805 const struct di_displayid_data_block *const *
di_displayid_get_data_blocks(const struct di_displayid * displayid)806 di_displayid_get_data_blocks(const struct di_displayid *displayid)
807 {
808 return (const struct di_displayid_data_block *const *) displayid->data_blocks;
809 }
810