1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/events/ozone/device/udev/device_manager_udev.h"
9 #include "base/debug/trace_event.h"
10 #include "base/strings/stringprintf.h"
11 #include "ui/events/ozone/device/device_event.h"
12 #include "ui/events/ozone/device/device_event_observer.h"
18 const char* kSubsystems[] = {
23 // Severity levels from syslog.h. We can't include it directly as it
24 // conflicts with base/logging.h
36 // Log handler for messages generated from libudev.
37 void UdevLog(struct udev* udev,
44 if (priority <= SYS_LOG_ERR)
45 LOG(ERROR) << "libudev: " << fn << ": " << base::StringPrintV(format, args);
46 else if (priority <= SYS_LOG_INFO)
47 VLOG(1) << "libudev: " << fn << ": " << base::StringPrintV(format, args);
49 VLOG(2) << "libudev: " << fn << ": " << base::StringPrintV(format, args);
52 // Create libudev context.
53 scoped_udev UdevCreate() {
54 struct udev* udev = udev_new();
56 udev_set_log_fn(udev, UdevLog);
57 udev_set_log_priority(udev, SYS_LOG_DEBUG);
59 return scoped_udev(udev);
62 // Start monitoring input device changes.
63 scoped_udev_monitor UdevCreateMonitor(struct udev* udev) {
64 struct udev_monitor* monitor = udev_monitor_new_from_netlink(udev, "udev");
66 for (size_t i = 0; i < arraysize(kSubsystems); ++i)
67 udev_monitor_filter_add_match_subsystem_devtype(
68 monitor, kSubsystems[i], NULL);
70 if (udev_monitor_enable_receiving(monitor))
71 LOG(ERROR) << "Failed to start receiving events from udev";
73 LOG(ERROR) << "Failed to create udev monitor";
76 return scoped_udev_monitor(monitor);
81 DeviceManagerUdev::DeviceManagerUdev()
82 : udev_(UdevCreate()),
83 monitor_(UdevCreateMonitor(udev_.get())) {
86 int fd = udev_monitor_get_fd(monitor_.get());
88 base::MessageLoopForUI::current()->WatchFileDescriptor(
89 fd, true, base::MessagePumpLibevent::WATCH_READ, &controller_, this);
93 DeviceManagerUdev::~DeviceManagerUdev() {}
95 void DeviceManagerUdev::ScanDevices(DeviceEventObserver* observer) {
96 scoped_udev_enumerate enumerate(udev_enumerate_new(udev_.get()));
100 for (size_t i = 0; i < arraysize(kSubsystems); ++i)
101 udev_enumerate_add_match_subsystem(enumerate.get(), kSubsystems[i]);
102 udev_enumerate_scan_devices(enumerate.get());
104 struct udev_list_entry* devices =
105 udev_enumerate_get_list_entry(enumerate.get());
106 struct udev_list_entry* entry;
108 udev_list_entry_foreach(entry, devices) {
109 scoped_udev_device device(udev_device_new_from_syspath(
110 udev_.get(), udev_list_entry_get_name(entry)));
114 scoped_ptr<DeviceEvent> event = ProcessMessage(device.get());
116 observer->OnDeviceEvent(*event.get());
120 void DeviceManagerUdev::AddObserver(DeviceEventObserver* observer) {
121 observers_.AddObserver(observer);
124 void DeviceManagerUdev::RemoveObserver(DeviceEventObserver* observer) {
125 observers_.RemoveObserver(observer);
128 void DeviceManagerUdev::OnFileCanReadWithoutBlocking(int fd) {
129 // The netlink socket should never become disconnected. There's no need
130 // to handle broken connections here.
131 TRACE_EVENT1("ozone", "UdevDeviceChange", "socket", fd);
133 scoped_udev_device device(udev_monitor_receive_device(monitor_.get()));
137 scoped_ptr<DeviceEvent> event = ProcessMessage(device.get());
140 DeviceEventObserver, observers_, OnDeviceEvent(*event.get()));
143 void DeviceManagerUdev::OnFileCanWriteWithoutBlocking(int fd) {
147 scoped_ptr<DeviceEvent> DeviceManagerUdev::ProcessMessage(udev_device* device) {
148 const char* path = udev_device_get_devnode(device);
149 const char* action = udev_device_get_action(device);
150 const char* hotplug = udev_device_get_property_value(device, "HOTPLUG");
151 const char* subsystem = udev_device_get_property_value(device, "SUBSYSTEM");
153 if (!path || !subsystem)
154 return scoped_ptr<DeviceEvent>();
156 DeviceEvent::DeviceType device_type;
157 if (!strcmp(subsystem, "input") &&
158 StartsWithASCII(path, "/dev/input/event", true))
159 device_type = DeviceEvent::INPUT;
160 else if (!strcmp(subsystem, "drm") && hotplug && !strcmp(hotplug, "1"))
161 device_type = DeviceEvent::DISPLAY;
163 return scoped_ptr<DeviceEvent>();
165 DeviceEvent::ActionType action_type;
166 if (!action || !strcmp(action, "add"))
167 action_type = DeviceEvent::ADD;
168 else if (!strcmp(action, "remove"))
169 action_type = DeviceEvent::REMOVE;
170 else if (!strcmp(action, "change"))
171 action_type = DeviceEvent::CHANGE;
173 return scoped_ptr<DeviceEvent>();
175 return scoped_ptr<DeviceEvent>(
176 new DeviceEvent(device_type, action_type, base::FilePath(path)));