On Wed, Apr 24, 2013 at 02:58:02PM +0100, Richard Hughes wrote: > 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 | 149 > +++++++++++++++++++++++++++++++++++++++++++++++++++ > src/compositor.h | 2 +- > 2 files changed, 150 insertions(+), 1 deletion(-)
Looks good, thanks. Kristian > diff --git a/src/compositor-drm.c b/src/compositor-drm.c > index da1ba79..c8016cd 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,143 @@ 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_number; > + > + /* 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_number = (uint32_t) data[EDID_OFFSET_SERIAL + 0]; > + serial_number += (uint32_t) data[EDID_OFFSET_SERIAL + 1] * 0x100; > + serial_number += (uint32_t) data[EDID_OFFSET_SERIAL + 2] * 0x10000; > + serial_number += (uint32_t) data[EDID_OFFSET_SERIAL + 3] * 0x1000000; > + if (serial_number > 0) > + sprintf(edid->serial_number, "%lu", (unsigned long) > serial_number); > + > + /* 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 void > +find_and_parse_output_edid(struct drm_compositor *ec, > + struct drm_output *output, > + drmModeConnector *connector) > +{ > + drmModePropertyBlobPtr edid_blob = NULL; > + drmModePropertyPtr property; > + int i; > + int rc; > + > + 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) > + return; > + > + 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_number = output->edid.serial_number; > + } > + drmModeFreePropertyBlob(edid_blob); > +} > + > static int > create_output_for_connector(struct drm_compositor *ec, > drmModeRes *resources, > @@ -1517,6 +1663,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_number = "unknown"; > wl_list_init(&output->base.mode_list); > > if (connector->connector_type < ARRAY_LENGTH(connector_type_names)) > @@ -1642,6 +1789,8 @@ create_output_for_connector(struct drm_compositor *ec, > > wl_list_insert(ec->base.output_list.prev, &output->base.link); > > + find_and_parse_output_edid(ec, output, connector); > + > 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..eb8ad82 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_number; > uint32_t subpixel; > uint32_t transform; > > -- > 1.8.2 > > _______________________________________________ > wayland-devel mailing list > wayland-devel@lists.freedesktop.org > http://lists.freedesktop.org/mailman/listinfo/wayland-devel _______________________________________________ wayland-devel mailing list wayland-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/wayland-devel