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. 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. 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. 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 81 DeviceManagerUdev::DeviceManagerUdev() : udev_(UdevCreate()) { 82 } 83 84 DeviceManagerUdev::~DeviceManagerUdev() { 85 } 86 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 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 126 void DeviceManagerUdev::AddObserver(DeviceEventObserver* observer) { 127 observers_.AddObserver(observer); 128 } 129 130 void DeviceManagerUdev::RemoveObserver(DeviceEventObserver* observer) { 131 observers_.RemoveObserver(observer); 132 } 133 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 150 void DeviceManagerUdev::OnFileCanWriteWithoutBlocking(int fd) { 151 NOTREACHED(); 152 } 153 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