Home | History | Annotate | Download | only in udev
      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