• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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