blob: 233180c93deadd5ec88bf898257ba8b25d0c5d87 [file] [log] [blame]
// Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "power_manager/powerd/system/display/display_watcher.h"
#include <algorithm>
#include <base/files/file_enumerator.h>
#include <base/file_util.h>
#include <base/logging.h>
#include <base/strings/string_util.h>
#include "power_manager/powerd/system/udev.h"
namespace power_manager {
namespace system {
namespace {
// Path containing directories describing the state of DRM devices.
const char kSysClassDrmPath[] = "/sys/class/drm";
// Glob-style pattern for device directories within kSysClassDrmPath.
const char kDrmDeviceNamePattern[] = "card*";
// Glob-style pattern for the I2C device name within a DRM device directory.
const char kI2CDeviceNamePattern[] = "i2c-*";
// Directory containing I2C devices.
const char kI2CDevPath[] = "/dev";
// Returns true if the device described by |drm_device_dir| is connected.
bool DeviceIsConnected(const base::FilePath& drm_device_dir) {
base::FilePath status_path =
drm_device_dir.Append(DisplayWatcher::kDrmStatusFile);
std::string status;
if (!base::ReadFileToString(status_path, &status))
return false;
// Trim whitespace to deal with trailing newlines.
TrimWhitespaceASCII(status, TRIM_TRAILING, &status);
return status == DisplayWatcher::kDrmStatusConnected;
}
} // namespace
const char DisplayWatcher::kI2CUdevSubsystem[] = "i2c-dev";
const char DisplayWatcher::kDrmUdevSubsystem[] = "drm";
const char DisplayWatcher::kDrmStatusFile[] = "status";
const char DisplayWatcher::kDrmStatusConnected[] = "connected";
DisplayWatcher::DisplayWatcher() : udev_(NULL) {}
DisplayWatcher::~DisplayWatcher() {
if (udev_) {
udev_->RemoveObserver(kI2CUdevSubsystem, this);
udev_->RemoveObserver(kDrmUdevSubsystem, this);
udev_ = NULL;
}
}
void DisplayWatcher::Init(UdevInterface* udev) {
udev_ = udev;
udev_->AddObserver(kI2CUdevSubsystem, this);
udev_->AddObserver(kDrmUdevSubsystem, this);
UpdateDisplays();
}
const std::vector<DisplayInfo>& DisplayWatcher::GetDisplays() const {
return displays_;
}
void DisplayWatcher::AddObserver(DisplayWatcherObserver* observer) {
observers_.AddObserver(observer);
}
void DisplayWatcher::RemoveObserver(DisplayWatcherObserver* observer) {
observers_.RemoveObserver(observer);
}
void DisplayWatcher::OnUdevEvent(const std::string& subsystem,
const std::string& sysname,
UdevObserver::Action action) {
VLOG(1) << "Got udev event for " << sysname << " on subsystem " << subsystem;
UpdateDisplays();
}
base::FilePath DisplayWatcher::GetI2CDevicePath(const base::FilePath& drm_dir) {
base::FileEnumerator enumerator(
drm_dir,
false, // recursive
base::FileEnumerator::DIRECTORIES,
kI2CDeviceNamePattern);
for (base::FilePath i2c_dir = enumerator.Next(); !i2c_dir.empty();
i2c_dir = enumerator.Next()) {
base::FilePath dev_path =
i2c_dev_path_for_testing_.empty() ? base::FilePath(kI2CDevPath) :
i2c_dev_path_for_testing_;
const std::string i2c_name = i2c_dir.BaseName().value();
base::FilePath i2c_dev = dev_path.Append(i2c_name);
if (base::PathExists(i2c_dev))
return i2c_dev;
}
return base::FilePath();
}
void DisplayWatcher::UpdateDisplays() {
std::vector<DisplayInfo> new_displays;
base::FileEnumerator enumerator(
sysfs_drm_path_for_testing_.empty() ?
base::FilePath(kSysClassDrmPath) :
sysfs_drm_path_for_testing_,
false, // recursive
static_cast<base::FileEnumerator::FileType>(
base::FileEnumerator::FILES |
base::FileEnumerator::DIRECTORIES |
base::FileEnumerator::SHOW_SYM_LINKS),
kDrmDeviceNamePattern);
for (base::FilePath device_path = enumerator.Next(); !device_path.empty();
device_path = enumerator.Next()) {
if (!DeviceIsConnected(device_path))
continue;
DisplayInfo info;
info.drm_path = device_path;
info.i2c_path = GetI2CDevicePath(device_path);
new_displays.push_back(info);
VLOG(1) << "Found connected display: drm_path=" << info.drm_path.value()
<< ", i2c_path=" << info.i2c_path.value();
}
std::sort(new_displays.begin(), new_displays.end());
if (new_displays != displays_) {
displays_.swap(new_displays);
FOR_EACH_OBSERVER(DisplayWatcherObserver, observers_,
OnDisplaysChanged(displays_));
}
}
} // namespace system
} // namespace power_manager