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

Reply via email to