]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
analyze-chid: Support EDID CHIDs 36843/head
authoranonymix007 <48598263+anonymix007@users.noreply.github.com>
Mon, 31 Mar 2025 17:41:15 +0000 (20:41 +0300)
committeranonymix007 <48598263+anonymix007@users.noreply.github.com>
Wed, 7 May 2025 15:52:49 +0000 (18:52 +0300)
man/systemd-analyze.xml
src/analyze/analyze-chid.c
src/analyze/analyze.c
src/analyze/analyze.h

index b3975d5f03e10779b732edf9f67d8b09a75ac5d6..e61c557fe9b7e1b40b9d71fe329d80b3817832f0 100644 (file)
@@ -1713,6 +1713,15 @@ LEGEND: M → sys_vendor (LENOVO) ┄ F → product_family (ThinkPad X1 Carbon G
         <xi:include href="version-info.xml" xpointer="v257"/></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--drm-device=<replaceable>PATH</replaceable></option></term>
+
+        <listitem><para>When provided with the <command>chid</command> command, use this sysfs path to a DRM
+        device to fetch EDID from. Example: <filename>/sys/class/drm/card1-HDMI-A-1/</filename></para>
+
+        <xi:include href="version-info.xml" xpointer="v258"/></listitem>
+      </varlistentry>
+
       <xi:include href="standard-options.xml" xpointer="help" />
       <xi:include href="standard-options.xml" xpointer="version" />
       <xi:include href="standard-options.xml" xpointer="no-pager" />
index 01e49965f8e53245719f66d8bdc1d5c3a4b6343f..6d2c74345f0fe7e128bcb4dc86b7f90037808f00 100644 (file)
@@ -1,9 +1,14 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+#include "sd-device.h"
+
 #include "alloc-util.h"
 #include "analyze.h"
 #include "analyze-chid.h"
 #include "chid-fundamental.h"
+#include "device-util.h"
+#include "dirent-util.h"
+#include "edid-fundamental.h"
 #include "efi-api.h"
 #include "escape.h"
 #include "fd-util.h"
 #include "virt.h"
 
 static int parse_chid_type(const char *s, size_t *ret) {
+        char *e;
         unsigned u;
         int r;
 
         assert(s);
 
-        r = safe_atou(s, &u);
-        if (r < 0)
-                return r;
-        if (u >= CHID_TYPES_MAX)
-                return -ERANGE;
+        if ((e = startswith(s, "ext"))) {
+                r = safe_atou(e, &u);
+                if (r < 0)
+                        return r;
+                if (u >= CHID_TYPES_MAX - EXTRA_CHID_BASE)
+                        return -ERANGE;
+                u += EXTRA_CHID_BASE;
+        } else {
+                r = safe_atou(s, &u);
+                if (r < 0)
+                        return r;
+                if (u >= EXTRA_CHID_BASE)
+                        return -ERANGE;
+        }
+
 
         if (ret)
                 *ret = u;
@@ -44,6 +60,7 @@ static const char *const chid_smbios_friendly[_CHID_SMBIOS_FIELDS_MAX] = {
         [CHID_SMBIOS_BIOS_MAJOR]             = "bios-major",
         [CHID_SMBIOS_BIOS_MINOR]             = "bios-minor",
         [CHID_SMBIOS_ENCLOSURE_TYPE]         = "enclosure-type",
+        [CHID_EDID_PANEL]                    = "edid-panel",
 };
 
 static const char chid_smbios_fields_char[_CHID_SMBIOS_FIELDS_MAX] = {
@@ -58,6 +75,7 @@ static const char chid_smbios_fields_char[_CHID_SMBIOS_FIELDS_MAX] = {
         [CHID_SMBIOS_BIOS_MAJOR]             = 'R',
         [CHID_SMBIOS_BIOS_MINOR]             = 'r',
         [CHID_SMBIOS_ENCLOSURE_TYPE]         = 'e',
+        [CHID_EDID_PANEL]                    = 'E',
 };
 
 static char *chid_smbios_fields_string(uint32_t combination) {
@@ -91,8 +109,14 @@ static int add_chid(Table *table, const EFI_GUID guids[static CHID_TYPES_MAX], s
         if (!flags)
                 return log_oom();
 
+        if (t < EXTRA_CHID_BASE)
+                r = table_add_many(table, TABLE_UINT, (unsigned) t);
+        else
+                r = table_add_cell_stringf(table, NULL, "ext%zu", t - EXTRA_CHID_BASE);
+        if (r < 0)
+                return table_log_add_error(r);
+
         r = table_add_many(table,
-                           TABLE_UINT, (unsigned) t,
                            TABLE_STRING, flags,
                            TABLE_UUID, id);
         if (r < 0)
@@ -224,6 +248,93 @@ static int smbios_fields_acquire(char16_t *fields[static _CHID_SMBIOS_FIELDS_MAX
         return 0;
 }
 
+static int edid_parse(sd_device *drm_dev, char16_t **ret_panel) {
+        const char *edid_content;
+        size_t edid_size;
+        int r;
+
+        assert(drm_dev);
+        assert(ret_panel);
+
+        r = sd_device_get_sysattr_value_with_size(drm_dev, "edid", &edid_content, &edid_size);
+        if (r < 0)
+                return r;
+        if (edid_size == 0)
+                return -ENXIO;
+
+        EdidHeader header;
+        if (edid_parse_blob(edid_content, edid_size, &header) < 0)
+                return -EBADMSG;
+
+        _cleanup_free_ char16_t *panel_id = new0(char16_t, 8);
+        if (!panel_id)
+                return -ENOMEM;
+
+        if (edid_get_panel_id(&header, panel_id) < 0)
+                return -EBADMSG;
+
+        *ret_panel = TAKE_PTR(panel_id);
+        return 0;
+}
+
+static int edid_search(char16_t **ret_panel) {
+        _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+        _cleanup_strv_free_ char **drm_paths = NULL;
+        _cleanup_free_ char16_t *unique_panel = NULL;
+        size_t n = 0;
+        int r;
+
+        assert(ret_panel);
+
+        r = sd_device_enumerator_new(&e);
+        if (r < 0)
+                return log_error_errno(r, "Failed to create device enumerator: %m");
+
+        r = sd_device_enumerator_allow_uninitialized(e);
+        if (r < 0)
+                return log_error_errno(r, "Failed to allow uninitialized device enumerator: %m");
+
+        r = sd_device_enumerator_add_match_subsystem(e, "drm", true);
+        if (r < 0)
+                return log_error_errno(r, "Failed to add drm match subsystem to device enumerator: %m");
+
+        FOREACH_DEVICE(e, d) {
+                _cleanup_free_ char16_t *panel = NULL;
+                const char *drm_path;
+
+                r = sd_device_get_syspath(d, &drm_path);
+                if (r < 0)
+                        return log_device_error_errno(d, r, "Failed to get syspath from device: %m");
+
+                r = edid_parse(d, &panel);
+                if (ERRNO_IS_DEVICE_ABSENT(r))
+                        continue;
+                if (r < 0) {
+                        log_device_debug_errno(d, r, "Failed to parse EDID from DRM device, skipping: %m");
+                        continue;
+                }
+
+                if (!unique_panel)
+                        unique_panel = TAKE_PTR(panel);
+
+                if (strv_extend_with_size(&drm_paths, &n, drm_path) < 0)
+                        return log_oom();
+        }
+
+        if (n == 1) {
+                *ret_panel = TAKE_PTR(unique_panel);
+                return 0;
+        }
+        if (n == 0)
+                return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "No monitors detected, skipping EDID CHID extensions.");
+
+        log_notice("Multiple monitors detected, skipping EDID CHID extensions.");
+        STRV_FOREACH(s, drm_paths)
+                log_info("Hint: use --drm-device=%s", *s);
+
+        return -ENOTUNIQ;
+}
+
 int verb_chid(int argc, char *argv[], void *userdata) {
 
         _cleanup_(table_unrefp) Table *table = NULL;
@@ -244,6 +355,24 @@ int verb_chid(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return r;
 
+        if (arg_drm_device_path) {
+                _cleanup_(sd_device_unrefp) sd_device *drm_dev = NULL;
+                r = sd_device_new_from_path(&drm_dev, arg_drm_device_path);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to open device %s: %m", arg_drm_device_path);
+
+                if (!device_in_subsystem(drm_dev, "drm"))
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot read EDID from a non-DRM device '%s'", arg_drm_device_path);
+
+                r = edid_parse(drm_dev, &smbios_fields[CHID_EDID_PANEL]);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse EDID for device %s: %m", arg_drm_device_path);
+        } else {
+                r = edid_search(&smbios_fields[CHID_EDID_PANEL]);
+                if (r < 0 && !IN_SET(r, -ENOTUNIQ, -ENODEV))
+                        return r;
+        }
+
         EFI_GUID chids[CHID_TYPES_MAX] = {};
         chid_calculate((const char16_t* const*) smbios_fields, chids);
 
@@ -258,7 +387,7 @@ int verb_chid(int argc, char *argv[], void *userdata) {
                         size_t t;
                         r = parse_chid_type(*as, &t);
                         if (r < 0)
-                                return log_error_errno(r, "Failed to pare CHID type: %s", *as);
+                                return log_error_errno(r, "Failed to parse CHID type: %s", *as);
 
                         r = add_chid(table, chids, t);
                         if (r < 0)
index 0b029c5f01764a7cf6e0b8b7782d0768847e9307..f6a19f306fa2619f014cb7114404800536637e7d 100644 (file)
@@ -121,12 +121,14 @@ char *arg_profile = NULL;
 bool arg_legend = true;
 bool arg_table = false;
 ImagePolicy *arg_image_policy = NULL;
+char *arg_drm_device_path = NULL;
 
 STATIC_DESTRUCTOR_REGISTER(arg_dot_from_patterns, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_dot_to_patterns, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_security_policy, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_drm_device_path, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_unit, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_profile, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
@@ -287,6 +289,8 @@ static int help(int argc, char *argv[], void *userdata) {
                "     --image=PATH            Operate on disk image as filesystem root\n"
                "     --image-policy=POLICY   Specify disk image dissection policy\n"
                "  -m --mask                  Parse parameter as numeric capability mask\n"
+               "     --drm-device=PATH       Use this DRM device sysfs path to get EDID\n"
+
                "\nSee the %2$s for details.\n",
                program_invocation_short_name,
                link,
@@ -333,6 +337,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_TLDR,
                 ARG_SCALE_FACTOR_SVG,
                 ARG_DETAILED_SVG,
+                ARG_DRM_DEVICE_PATH,
         };
 
         static const struct option options[] = {
@@ -371,6 +376,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "mask",             no_argument,       NULL, 'm'                  },
                 { "scale-svg",        required_argument, NULL, ARG_SCALE_FACTOR_SVG },
                 { "detailed",         no_argument,       NULL, ARG_DETAILED_SVG     },
+                { "drm-device",       required_argument, NULL, ARG_DRM_DEVICE_PATH  },
                 {}
         };
 
@@ -580,6 +586,12 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_detailed_svg = true;
                         break;
 
+                case ARG_DRM_DEVICE_PATH:
+                        r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_drm_device_path);
+                        if (r < 0)
+                                return r;
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -638,6 +650,9 @@ static int parse_argv(int argc, char *argv[]) {
         if (arg_capability != CAPABILITY_LITERAL && !streq_ptr(argv[optind], "capability"))
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --mask is only supported for capability.");
 
+        if (arg_drm_device_path && !streq_ptr(argv[optind], "chid"))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --drm-device is only supported for chid right now.");
+
         return 1; /* work to do */
 }
 
index 959b8a1b8508324ed567883da556a4401127fab9..1ab018e071167f926ff287a6cc75d5c61b4055ef 100644 (file)
@@ -51,6 +51,7 @@ extern char *arg_profile;
 extern bool arg_legend;
 extern bool arg_table;
 extern ImagePolicy *arg_image_policy;
+extern char *arg_drm_device_path;
 
 int acquire_bus(sd_bus **bus, bool *use_full_bus);