diff options
Diffstat (limited to 'chromium/ui/events/ozone/device/udev/device_manager_udev.cc')
-rw-r--r-- | chromium/ui/events/ozone/device/udev/device_manager_udev.cc | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/chromium/ui/events/ozone/device/udev/device_manager_udev.cc b/chromium/ui/events/ozone/device/udev/device_manager_udev.cc new file mode 100644 index 00000000000..7f86bee4f84 --- /dev/null +++ b/chromium/ui/events/ozone/device/udev/device_manager_udev.cc @@ -0,0 +1,186 @@ +// Copyright 2014 The Chromium 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 "ui/events/ozone/device/udev/device_manager_udev.h" + +#include <libudev.h> + +#include "base/debug/trace_event.h" +#include "base/strings/stringprintf.h" +#include "ui/events/ozone/device/device_event.h" +#include "ui/events/ozone/device/device_event_observer.h" + +namespace ui { + +namespace { + +const char* kSubsystems[] = { + "input", + "drm", +}; + +// Severity levels from syslog.h. We can't include it directly as it +// conflicts with base/logging.h +enum { + SYS_LOG_EMERG = 0, + SYS_LOG_ALERT = 1, + SYS_LOG_CRIT = 2, + SYS_LOG_ERR = 3, + SYS_LOG_WARNING = 4, + SYS_LOG_NOTICE = 5, + SYS_LOG_INFO = 6, + SYS_LOG_DEBUG = 7, +}; + +// Log handler for messages generated from libudev. +void UdevLog(struct udev* udev, + int priority, + const char* file, + int line, + const char* fn, + const char* format, + va_list args) { + if (priority <= SYS_LOG_ERR) + LOG(ERROR) << "libudev: " << fn << ": " << base::StringPrintV(format, args); + else if (priority <= SYS_LOG_INFO) + VLOG(1) << "libudev: " << fn << ": " << base::StringPrintV(format, args); + else // SYS_LOG_DEBUG + VLOG(2) << "libudev: " << fn << ": " << base::StringPrintV(format, args); +} + +// Create libudev context. +device::ScopedUdevPtr UdevCreate() { + struct udev* udev = udev_new(); + if (udev) { + udev_set_log_fn(udev, UdevLog); + udev_set_log_priority(udev, SYS_LOG_DEBUG); + } + return device::ScopedUdevPtr(udev); +} + +// Start monitoring input device changes. +device::ScopedUdevMonitorPtr UdevCreateMonitor(struct udev* udev) { + struct udev_monitor* monitor = udev_monitor_new_from_netlink(udev, "udev"); + if (monitor) { + for (size_t i = 0; i < arraysize(kSubsystems); ++i) + udev_monitor_filter_add_match_subsystem_devtype( + monitor, kSubsystems[i], NULL); + + if (udev_monitor_enable_receiving(monitor)) + LOG(ERROR) << "Failed to start receiving events from udev"; + } else { + LOG(ERROR) << "Failed to create udev monitor"; + } + + return device::ScopedUdevMonitorPtr(monitor); +} + +} // namespace + +DeviceManagerUdev::DeviceManagerUdev() : udev_(UdevCreate()) { +} + +DeviceManagerUdev::~DeviceManagerUdev() { +} + +void DeviceManagerUdev::CreateMonitor() { + if (monitor_) + return; + monitor_ = UdevCreateMonitor(udev_.get()); + if (monitor_) { + int fd = udev_monitor_get_fd(monitor_.get()); + CHECK_GT(fd, 0); + base::MessageLoopForUI::current()->WatchFileDescriptor( + fd, true, base::MessagePumpLibevent::WATCH_READ, &controller_, this); + } +} + +void DeviceManagerUdev::ScanDevices(DeviceEventObserver* observer) { + CreateMonitor(); + + device::ScopedUdevEnumeratePtr enumerate(udev_enumerate_new(udev_.get())); + if (!enumerate) + return; + + for (size_t i = 0; i < arraysize(kSubsystems); ++i) + udev_enumerate_add_match_subsystem(enumerate.get(), kSubsystems[i]); + udev_enumerate_scan_devices(enumerate.get()); + + struct udev_list_entry* devices = + udev_enumerate_get_list_entry(enumerate.get()); + struct udev_list_entry* entry; + + udev_list_entry_foreach(entry, devices) { + device::ScopedUdevDevicePtr device(udev_device_new_from_syspath( + udev_.get(), udev_list_entry_get_name(entry))); + if (!device) + continue; + + scoped_ptr<DeviceEvent> event = ProcessMessage(device.get()); + if (event) + observer->OnDeviceEvent(*event.get()); + } +} + +void DeviceManagerUdev::AddObserver(DeviceEventObserver* observer) { + observers_.AddObserver(observer); +} + +void DeviceManagerUdev::RemoveObserver(DeviceEventObserver* observer) { + observers_.RemoveObserver(observer); +} + +void DeviceManagerUdev::OnFileCanReadWithoutBlocking(int fd) { + // The netlink socket should never become disconnected. There's no need + // to handle broken connections here. + TRACE_EVENT1("ozone", "UdevDeviceChange", "socket", fd); + + device::ScopedUdevDevicePtr device( + udev_monitor_receive_device(monitor_.get())); + if (!device) + return; + + scoped_ptr<DeviceEvent> event = ProcessMessage(device.get()); + if (event) + FOR_EACH_OBSERVER( + DeviceEventObserver, observers_, OnDeviceEvent(*event.get())); +} + +void DeviceManagerUdev::OnFileCanWriteWithoutBlocking(int fd) { + NOTREACHED(); +} + +scoped_ptr<DeviceEvent> DeviceManagerUdev::ProcessMessage(udev_device* device) { + const char* path = udev_device_get_devnode(device); + const char* action = udev_device_get_action(device); + const char* hotplug = udev_device_get_property_value(device, "HOTPLUG"); + const char* subsystem = udev_device_get_property_value(device, "SUBSYSTEM"); + + if (!path || !subsystem) + return scoped_ptr<DeviceEvent>(); + + DeviceEvent::DeviceType device_type; + if (!strcmp(subsystem, "input") && + StartsWithASCII(path, "/dev/input/event", true)) + device_type = DeviceEvent::INPUT; + else if (!strcmp(subsystem, "drm") && hotplug && !strcmp(hotplug, "1")) + device_type = DeviceEvent::DISPLAY; + else + return scoped_ptr<DeviceEvent>(); + + DeviceEvent::ActionType action_type; + if (!action || !strcmp(action, "add")) + action_type = DeviceEvent::ADD; + else if (!strcmp(action, "remove")) + action_type = DeviceEvent::REMOVE; + else if (!strcmp(action, "change")) + action_type = DeviceEvent::CHANGE; + else + return scoped_ptr<DeviceEvent>(); + + return scoped_ptr<DeviceEvent>( + new DeviceEvent(device_type, action_type, base::FilePath(path))); +} + +} // namespace ui |