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