mirror of
https://github.com/morgan9e/systemd
synced 2026-04-14 00:14:32 +09:00
analyze-chid: Support EDID CHIDs
This commit is contained in:
@@ -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" />
|
||||
|
||||
@@ -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"
|
||||
@@ -15,16 +20,27 @@
|
||||
#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)
|
||||
|
||||
@@ -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 */
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user