1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
|
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/display/manager/display_port_observer.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/task/thread_pool.h"
#include "base/threading/scoped_blocking_call.h"
#include "third_party/re2/src/re2/re2.h"
#include "ui/display/types/display_snapshot.h"
namespace display {
namespace {
const LazyRE2 kTypecConnUeventPattern = {R"(TYPEC_PORT=port(\d+))"};
std::vector<uint32_t> ParseDrmSysfsAndFindPort(
const std::vector<std::pair<uint64_t, base::FilePath>>&
base_connector_id_and_syspath) {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
std::vector<uint32_t> port_nums;
// Each pair is from each DisplaySnapshot.
for (const auto& pair : base_connector_id_and_syspath) {
auto base_connector_id = pair.first;
const auto& sys_path = pair.second;
// `sys_path` of a DRM device, i.e. /sys/class/drm/cardX.
base::FileEnumerator enumerator(sys_path, false,
base::FileEnumerator::DIRECTORIES,
FILE_PATH_LITERAL("card*-DP-*"));
// Each directory in `sys_path` represents a connector, so we fetch each
// connector's ID by reading the `/sys/class/drm/*/connector_id` file
// (e.g. /sys/class/drm/card0/card0-DP-1/connector_id stores 256). We use
// the fetched connector id to compare and match the corresponding base
// connector (root of MST hub tree) per each DisplaySnapshot.
for (auto path = enumerator.Next(); !path.empty();
path = enumerator.Next()) {
std::string connector_id_str;
uint64_t connector_id_int;
if (!base::ReadFileToString(path.Append("connector_id"),
&connector_id_str)) {
continue;
}
base::TrimWhitespaceASCII(connector_id_str, base::TRIM_ALL,
&connector_id_str);
if (!base::StringToUint64(connector_id_str, &connector_id_int)) {
LOG(WARNING) << "Invalid connector id " << connector_id_str << " at "
<< path.value();
continue;
}
if (connector_id_int != base_connector_id) {
continue;
}
// A connector with a matching id is found at this point, so break the
// loop beyond here as there is no need to further look for a connector.
// We find the USB-C port which the DP connector is routed to by
// referring to the symlink to Type C connector at `typec_connector`
// (i.e. /sys/class/drm/cardX/cardX-DP-X/typec_connector). This symlink
// may not be present for older kernel and firmware.
std::string typec_conn_uevent;
uint32_t port_num;
if (!base::ReadFileToString(path.Append("typec_connector/uevent"),
&typec_conn_uevent)) {
break;
}
if (!RE2::PartialMatch(typec_conn_uevent, *kTypecConnUeventPattern,
&port_num)) {
break;
}
port_nums.push_back(port_num);
break;
}
}
return port_nums;
}
} // namespace
DisplayPortObserver::DisplayPortObserver(
DisplayConfigurator* configurator,
base::RepeatingCallback<void(const std::vector<uint32_t>&)>
on_port_change_callback)
: configurator_(configurator),
on_port_change_callback_(on_port_change_callback) {
if (configurator_) {
configurator_->AddObserver(this);
}
}
DisplayPortObserver::~DisplayPortObserver() {
if (configurator_) {
configurator_->RemoveObserver(this);
}
}
void DisplayPortObserver::OnDisplayConfigurationChanged(
const DisplayConfigurator::DisplayStateList& display_states) {
// If no changes in base connector ids, then assume no changes on ports, so
// return early to prevent overkill.
std::set<uint64_t> base_connector_ids_;
for (display::DisplaySnapshot* state : display_states) {
base_connector_ids_.insert(state->base_connector_id());
}
if (base_connector_ids_ == prev_base_connector_ids_) {
return;
}
prev_base_connector_ids_ = base_connector_ids_;
// For each DisplaySnapshot, extract base_connector_id and sys_path.
std::vector<std::pair<uint64_t, base::FilePath>>
base_connector_id_and_syspath;
for (display::DisplaySnapshot* state : display_states) {
base_connector_id_and_syspath.push_back(
std::make_pair(state->base_connector_id(), state->sys_path()));
}
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE,
{base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
base::BindOnce(&ParseDrmSysfsAndFindPort, base_connector_id_and_syspath),
base::BindOnce(&DisplayPortObserver::SetTypeCPortsUsingDisplays,
weak_ptr_factory_.GetWeakPtr()));
}
void DisplayPortObserver::OnDisplayConfigurationChangeFailed(
const DisplayConfigurator::DisplayStateList& displays,
MultipleDisplayState failed_new_state) {}
void DisplayPortObserver::SetTypeCPortsUsingDisplays(
std::vector<uint32_t> port_nums) {
on_port_change_callback_.Run(port_nums);
}
} // namespace display
|