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