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.
4
5 #include "ui/events/ozone/device/udev/device_manager_udev.h"
6
7 #include <stddef.h>
8
9 #include "base/logging.h"
10 #include "base/stl_util.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/task/current_thread.h"
13 #include "base/trace_event/trace_event.h"
14 #include "ui/events/ozone/device/device_event.h"
15 #include "ui/events/ozone/device/device_event_observer.h"
16
17 namespace ui {
18
19 namespace {
20
21 const char* const kSubsystems[] = {
22 "input",
23 "drm",
24 };
25
26 // Severity levels from syslog.h. We can't include it directly as it
27 // conflicts with base/logging.h
28 enum {
29 SYS_LOG_EMERG = 0,
30 SYS_LOG_ALERT = 1,
31 SYS_LOG_CRIT = 2,
32 SYS_LOG_ERR = 3,
33 SYS_LOG_WARNING = 4,
34 SYS_LOG_NOTICE = 5,
35 SYS_LOG_INFO = 6,
36 SYS_LOG_DEBUG = 7,
37 };
38
39 // Log handler for messages generated from libudev.
UdevLog(struct udev * udev,int priority,const char * file,int line,const char * fn,const char * format,va_list args)40 void UdevLog(struct udev* udev,
41 int priority,
42 const char* file,
43 int line,
44 const char* fn,
45 const char* format,
46 va_list args) {
47 if (priority <= SYS_LOG_ERR)
48 LOG(ERROR) << "libudev: " << fn << ": " << base::StringPrintV(format, args);
49 else if (priority <= SYS_LOG_INFO)
50 VLOG(1) << "libudev: " << fn << ": " << base::StringPrintV(format, args);
51 else // SYS_LOG_DEBUG
52 VLOG(2) << "libudev: " << fn << ": " << base::StringPrintV(format, args);
53 }
54
55 // Create libudev context.
UdevCreate()56 device::ScopedUdevPtr UdevCreate() {
57 struct udev* udev = device::udev_new();
58 if (udev) {
59 device::udev_set_log_fn(udev, UdevLog);
60 device::udev_set_log_priority(udev, SYS_LOG_DEBUG);
61 }
62 return device::ScopedUdevPtr(udev);
63 }
64
65 // Start monitoring input device changes.
UdevCreateMonitor(struct udev * udev)66 device::ScopedUdevMonitorPtr UdevCreateMonitor(struct udev* udev) {
67 struct udev_monitor* monitor =
68 device::udev_monitor_new_from_netlink(udev, "udev");
69 if (monitor) {
70 for (size_t i = 0; i < base::size(kSubsystems); ++i)
71 device::udev_monitor_filter_add_match_subsystem_devtype(
72 monitor, kSubsystems[i], NULL);
73
74 if (device::udev_monitor_enable_receiving(monitor))
75 LOG(ERROR) << "Failed to start receiving events from udev";
76 } else {
77 LOG(ERROR) << "Failed to create udev monitor";
78 }
79
80 return device::ScopedUdevMonitorPtr(monitor);
81 }
82
83 } // namespace
84
DeviceManagerUdev()85 DeviceManagerUdev::DeviceManagerUdev()
86 : udev_(UdevCreate()), controller_(FROM_HERE) {}
87
~DeviceManagerUdev()88 DeviceManagerUdev::~DeviceManagerUdev() {
89 }
90
CreateMonitor()91 void DeviceManagerUdev::CreateMonitor() {
92 if (monitor_)
93 return;
94 monitor_ = UdevCreateMonitor(udev_.get());
95 if (monitor_) {
96 int fd = device::udev_monitor_get_fd(monitor_.get());
97 CHECK_GT(fd, 0);
98 base::CurrentUIThread::Get()->WatchFileDescriptor(
99 fd, true, base::MessagePumpForUI::WATCH_READ, &controller_, this);
100 }
101 }
102
ScanDevices(DeviceEventObserver * observer)103 void DeviceManagerUdev::ScanDevices(DeviceEventObserver* observer) {
104 CreateMonitor();
105
106 device::ScopedUdevEnumeratePtr enumerate(
107 device::udev_enumerate_new(udev_.get()));
108 if (!enumerate)
109 return;
110
111 for (size_t i = 0; i < base::size(kSubsystems); ++i)
112 device::udev_enumerate_add_match_subsystem(enumerate.get(), kSubsystems[i]);
113 device::udev_enumerate_scan_devices(enumerate.get());
114
115 struct udev_list_entry* devices =
116 device::udev_enumerate_get_list_entry(enumerate.get());
117 struct udev_list_entry* entry;
118
119 udev_list_entry_foreach(entry, devices) {
120 device::ScopedUdevDevicePtr device(device::udev_device_new_from_syspath(
121 udev_.get(), device::udev_list_entry_get_name(entry)));
122 if (!device)
123 continue;
124
125 std::unique_ptr<DeviceEvent> event = ProcessMessage(device.get());
126 if (event)
127 observer->OnDeviceEvent(*event.get());
128 }
129 }
130
AddObserver(DeviceEventObserver * observer)131 void DeviceManagerUdev::AddObserver(DeviceEventObserver* observer) {
132 observers_.AddObserver(observer);
133 }
134
RemoveObserver(DeviceEventObserver * observer)135 void DeviceManagerUdev::RemoveObserver(DeviceEventObserver* observer) {
136 observers_.RemoveObserver(observer);
137 }
138
OnFileCanReadWithoutBlocking(int fd)139 void DeviceManagerUdev::OnFileCanReadWithoutBlocking(int fd) {
140 // The netlink socket should never become disconnected. There's no need
141 // to handle broken connections here.
142 TRACE_EVENT1("evdev", "UdevDeviceChange", "socket", fd);
143
144 device::ScopedUdevDevicePtr device(
145 device::udev_monitor_receive_device(monitor_.get()));
146 if (!device)
147 return;
148
149 std::unique_ptr<DeviceEvent> event = ProcessMessage(device.get());
150 if (event)
151 for (DeviceEventObserver& observer : observers_)
152 observer.OnDeviceEvent(*event.get());
153 }
154
OnFileCanWriteWithoutBlocking(int fd)155 void DeviceManagerUdev::OnFileCanWriteWithoutBlocking(int fd) {
156 NOTREACHED();
157 }
158
ProcessMessage(udev_device * device)159 std::unique_ptr<DeviceEvent> DeviceManagerUdev::ProcessMessage(
160 udev_device* device) {
161 const char* path = device::udev_device_get_devnode(device);
162 const char* action = device::udev_device_get_action(device);
163 const char* subsystem =
164 device::udev_device_get_property_value(device, "SUBSYSTEM");
165
166 if (!path || !subsystem)
167 return nullptr;
168
169 DeviceEvent::DeviceType device_type;
170 if (!strcmp(subsystem, "input") &&
171 base::StartsWith(path, "/dev/input/event", base::CompareCase::SENSITIVE))
172 device_type = DeviceEvent::INPUT;
173 else if (!strcmp(subsystem, "drm") &&
174 base::StartsWith(path, "/dev/dri/card",
175 base::CompareCase::SENSITIVE))
176 device_type = DeviceEvent::DISPLAY;
177 else
178 return nullptr;
179
180 DeviceEvent::ActionType action_type;
181 if (!action || !strcmp(action, "add"))
182 action_type = DeviceEvent::ADD;
183 else if (!strcmp(action, "remove"))
184 action_type = DeviceEvent::REMOVE;
185 else if (!strcmp(action, "change"))
186 action_type = DeviceEvent::CHANGE;
187 else
188 return nullptr;
189
190 return std::make_unique<DeviceEvent>(device_type, action_type,
191 base::FilePath(path));
192 }
193
194 } // namespace ui
195