[PATCH] Extract and parse the EDID when outputs are added

Richard Hughes hughsient at gmail.com
Mon Apr 22 04:57:01 PDT 2013


At the moment we're only extracting interesting strings. We have to be quite
careful parsing the EDID data, as vendors like to do insane things.

The original EDID parsing code was written by me for gnome-color-manager.
---
 src/compositor-drm.c | 139 +++++++++++++++++++++++++++++++++++++++++++++++++++
 src/compositor.h     |   2 +-
 2 files changed, 140 insertions(+), 1 deletion(-)

diff --git a/src/compositor-drm.c b/src/compositor-drm.c
index da1ba79..1909712 100644
--- a/src/compositor-drm.c
+++ b/src/compositor-drm.c
@@ -25,6 +25,7 @@
 
 #include <errno.h>
 #include <stdlib.h>
+#include <ctype.h>
 #include <string.h>
 #include <fcntl.h>
 #include <unistd.h>
@@ -131,6 +132,13 @@ struct drm_fb {
 	void *map;
 };
 
+struct drm_edid {
+	char eisa_id[13];
+	char monitor_name[13];
+	char pnp_id[5];
+	char serial_number[13];
+};
+
 struct drm_output {
 	struct weston_output   base;
 
@@ -139,6 +147,7 @@ struct drm_output {
 	int pipe;
 	uint32_t connector_id;
 	drmModeCrtcPtr original_crtc;
+	struct drm_edid edid;
 
 	int vblank_pending;
 	int page_flip_pending;
@@ -1486,6 +1495,101 @@ drm_output_fini_pixman(struct drm_output *output)
 	}
 }
 
+static void
+edid_parse_string (const uint8_t *data, char text[])
+{
+	int i;
+	int replaced = 0;
+
+	/* this is always 12 bytes, but we can't guarantee it's null
+	 * terminated or not junk. */
+	strncpy(text, (const char *) data, 12);
+
+	/* remove insane chars */
+	for (i = 0; text[i] != '\0'; i++) {
+		if (text[i] == '\n' ||
+		    text[i] == '\r') {
+			text[i] = '\0';
+			break;
+		}
+	}
+
+	/* ensure string is printable */
+	for (i = 0; text[i] != '\0'; i++) {
+		if (!isprint(text[i])) {
+			text[i] = '-';
+			replaced++;
+		}
+	}
+
+	/* if the string is random junk, ignore the string */
+	if (replaced > 4)
+		text[0] = '\0';
+}
+
+#define EDID_DESCRIPTOR_ALPHANUMERIC_DATA_STRING	0xfe
+#define EDID_DESCRIPTOR_DISPLAY_PRODUCT_NAME		0xfc
+#define EDID_DESCRIPTOR_DISPLAY_PRODUCT_SERIAL_NUMBER	0xff
+#define EDID_OFFSET_DATA_BLOCKS				0x36
+#define EDID_OFFSET_LAST_BLOCK				0x6c
+#define EDID_OFFSET_PNPID				0x08
+#define EDID_OFFSET_SERIAL				0x0c
+
+static int
+edid_parse (struct drm_edid *edid, const uint8_t *data, size_t length)
+{
+	int i;
+	uint32_t serial;
+
+	/* check header */
+	if (length < 128)
+		return -1;
+	if (data[0] != 0x00 || data[1] != 0xff)
+		return -1;
+
+	/* decode the PNP ID from three 5 bit words packed into 2 bytes
+	 * /--08--\/--09--\
+	 * 7654321076543210
+	 * |\---/\---/\---/
+	 * R  C1   C2   C3 */
+	edid->pnp_id[0] = 'A' + ((data[EDID_OFFSET_PNPID + 0] & 0x7c) / 4) - 1;
+	edid->pnp_id[1] = 'A' + ((data[EDID_OFFSET_PNPID + 0] & 0x3) * 8) + ((data[EDID_OFFSET_PNPID + 1] & 0xe0) / 32) - 1;
+	edid->pnp_id[2] = 'A' + (data[EDID_OFFSET_PNPID + 1] & 0x1f) - 1;
+	edid->pnp_id[3] = '\0';
+
+	/* maybe there isn't a ASCII serial number descriptor, so use this instead */
+	serial = (uint32_t) data[EDID_OFFSET_SERIAL + 0];
+	serial += (uint32_t) data[EDID_OFFSET_SERIAL + 1] * 0x100;
+	serial += (uint32_t) data[EDID_OFFSET_SERIAL + 2] * 0x10000;
+	serial += (uint32_t) data[EDID_OFFSET_SERIAL + 3] * 0x1000000;
+	if (serial > 0)
+		sprintf(edid->serial_number, "%lu", (unsigned long) serial);
+
+	/* parse EDID data */
+	for (i = EDID_OFFSET_DATA_BLOCKS;
+	     i <= EDID_OFFSET_LAST_BLOCK;
+	     i += 18) {
+		/* ignore pixel clock data */
+		if (data[i] != 0)
+			continue;
+		if (data[i+2] != 0)
+			continue;
+
+		/* any useful blocks? */
+		if (data[i+3] == EDID_DESCRIPTOR_DISPLAY_PRODUCT_NAME) {
+			edid_parse_string(&data[i+5],
+					  edid->monitor_name);
+		} else if (data[i+3] == EDID_DESCRIPTOR_DISPLAY_PRODUCT_SERIAL_NUMBER) {
+			edid_parse_string(&data[i+5],
+					  edid->serial_number);
+		} else if (data[i+3] == EDID_DESCRIPTOR_ALPHANUMERIC_DATA_STRING) {
+			edid_parse_string(&data[i+5],
+					  edid->eisa_id);
+		}
+	}
+	return 0;
+}
+
 static int
 create_output_for_connector(struct drm_compositor *ec,
 			    drmModeRes *resources,
@@ -1499,7 +1603,10 @@ create_output_for_connector(struct drm_compositor *ec,
 	drmModeEncoder *encoder;
 	drmModeModeInfo crtc_mode;
 	drmModeCrtc *crtc;
+	drmModePropertyPtr property;
+	drmModePropertyBlobPtr edid_blob = NULL;
 	int i;
+	int rc;
 	char name[32];
 	const char *type_name;
 
@@ -1517,6 +1624,7 @@ create_output_for_connector(struct drm_compositor *ec,
 	output->base.subpixel = drm_subpixel_to_wayland(connector->subpixel);
 	output->base.make = "unknown";
 	output->base.model = "unknown";
+	output->base.serial = "unknown";
 	wl_list_init(&output->base.mode_list);
 
 	if (connector->connector_type < ARRAY_LENGTH(connector_type_names))
@@ -1642,6 +1750,37 @@ create_output_for_connector(struct drm_compositor *ec,
 
 	wl_list_insert(ec->base.output_list.prev, &output->base.link);
 
+	/* find and parse the EDID blob */
+	for (i = 0; i < connector->count_props && !edid_blob; i++) {
+		property = drmModeGetProperty(ec->drm.fd, connector->props[i]);
+		if (!property)
+			continue;
+		if ((property->flags & DRM_MODE_PROP_BLOB) &&
+		    !strcmp(property->name, "EDID")) {
+			edid_blob = drmModeGetPropertyBlob(ec->drm.fd,
+							   connector->prop_values[i]);
+		}
+		drmModeFreeProperty(property);
+	}
+	if (edid_blob) {
+		rc = edid_parse(&output->edid,
+				edid_blob->data,
+				edid_blob->length);
+		if (!rc) {
+			weston_log("EDID data '%s', '%s', '%s'\n",
+				   output->edid.pnp_id,
+				   output->edid.monitor_name,
+				   output->edid.serial_number);
+			if (output->edid.pnp_id[0] != '\0')
+				output->base.make = output->edid.pnp_id;
+			if (output->edid.monitor_name[0] != '\0')
+				output->base.model = output->edid.monitor_name;
+			if (output->edid.serial_number[0] != '\0')
+				output->base.serial = output->edid.serial_number;
+		}
+		drmModeFreePropertyBlob(edid_blob);
+	}
+
 	output->base.origin = output->base.current;
 	output->base.start_repaint_loop = drm_output_start_repaint_loop;
 	output->base.repaint = drm_output_repaint;
diff --git a/src/compositor.h b/src/compositor.h
index 1e999a6..fa6162c 100644
--- a/src/compositor.h
+++ b/src/compositor.h
@@ -178,7 +178,7 @@ struct weston_output {
 	uint32_t frame_time;
 	int disable_planes;
 
-	char *make, *model;
+	char *make, *model, *serial;
 	uint32_t subpixel;
 	uint32_t transform;
 	
-- 
1.8.2



More information about the wayland-devel mailing list