Home | History | Annotate | Download | only in hid
      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 "device/hid/device_monitor_linux.h"
      6 
      7 #include <libudev.h>
      8 
      9 #include "base/lazy_instance.h"
     10 #include "base/logging.h"
     11 #include "base/threading/thread_restrictions.h"
     12 
     13 namespace device {
     14 
     15 namespace {
     16 
     17 const char kUdevName[] = "udev";
     18 const char kUdevActionAdd[] = "add";
     19 const char kUdevActionRemove[] = "remove";
     20 
     21 // The instance will be reset when message loop destroys.
     22 base::LazyInstance<scoped_ptr<DeviceMonitorLinux> >::Leaky
     23     g_device_monitor_linux_ptr = LAZY_INSTANCE_INITIALIZER;
     24 
     25 }  // namespace
     26 
     27 DeviceMonitorLinux::DeviceMonitorLinux() : monitor_fd_(-1) {
     28   base::ThreadRestrictions::AssertIOAllowed();
     29   base::MessageLoop::current()->AddDestructionObserver(this);
     30 
     31   udev_.reset(udev_new());
     32   if (!udev_) {
     33     LOG(ERROR) << "Failed to create udev.";
     34     return;
     35   }
     36   monitor_.reset(udev_monitor_new_from_netlink(udev_.get(), kUdevName));
     37   if (!monitor_) {
     38     LOG(ERROR) << "Failed to create udev monitor.";
     39     return;
     40   }
     41 
     42   int ret = udev_monitor_enable_receiving(monitor_.get());
     43   if (ret != 0) {
     44     LOG(ERROR) << "Failed to start udev monitoring.";
     45     return;
     46   }
     47 
     48   monitor_fd_ = udev_monitor_get_fd(monitor_.get());
     49   if (monitor_fd_ <= 0) {
     50     LOG(ERROR) << "Failed to start udev monitoring.";
     51     return;
     52   }
     53 
     54   if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
     55           monitor_fd_,
     56           true,
     57           base::MessageLoopForIO::WATCH_READ,
     58           &monitor_watcher_,
     59           this)) {
     60     return;
     61   }
     62 }
     63 
     64 // static
     65 DeviceMonitorLinux* DeviceMonitorLinux::GetInstance() {
     66   if (!HasInstance())
     67     g_device_monitor_linux_ptr.Get().reset(new DeviceMonitorLinux());
     68   return g_device_monitor_linux_ptr.Get().get();
     69 }
     70 
     71 // static
     72 bool DeviceMonitorLinux::HasInstance() {
     73   return g_device_monitor_linux_ptr.Get().get();
     74 }
     75 
     76 void DeviceMonitorLinux::AddObserver(Observer* observer) {
     77   DCHECK(thread_checker_.CalledOnValidThread());
     78   if (observer)
     79     observers_.AddObserver(observer);
     80 }
     81 
     82 void DeviceMonitorLinux::RemoveObserver(Observer* observer) {
     83   DCHECK(thread_checker_.CalledOnValidThread());
     84   if (observer)
     85     observers_.RemoveObserver(observer);
     86 }
     87 
     88 ScopedUdevDevicePtr DeviceMonitorLinux::GetDeviceFromPath(
     89     const std::string& path) {
     90   DCHECK(thread_checker_.CalledOnValidThread());
     91   ScopedUdevDevicePtr device(
     92       udev_device_new_from_syspath(udev_.get(), path.c_str()));
     93   return device.Pass();
     94 }
     95 
     96 void DeviceMonitorLinux::Enumerate(const EnumerateCallback& callback) {
     97   DCHECK(thread_checker_.CalledOnValidThread());
     98   ScopedUdevEnumeratePtr enumerate(udev_enumerate_new(udev_.get()));
     99 
    100   if (!enumerate) {
    101     LOG(ERROR) << "Failed to enumerate devices.";
    102     return;
    103   }
    104 
    105   if (udev_enumerate_scan_devices(enumerate.get()) != 0) {
    106     LOG(ERROR) << "Failed to enumerate devices.";
    107     return;
    108   }
    109 
    110   // This list is managed by |enumerate|.
    111   udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate.get());
    112   for (udev_list_entry* i = devices; i != NULL;
    113        i = udev_list_entry_get_next(i)) {
    114     ScopedUdevDevicePtr device(
    115         udev_device_new_from_syspath(udev_.get(), udev_list_entry_get_name(i)));
    116     if (device)
    117       callback.Run(device.get());
    118   }
    119 }
    120 
    121 void DeviceMonitorLinux::WillDestroyCurrentMessageLoop() {
    122   DCHECK(thread_checker_.CalledOnValidThread());
    123   g_device_monitor_linux_ptr.Get().reset(NULL);
    124 }
    125 
    126 void DeviceMonitorLinux::OnFileCanReadWithoutBlocking(int fd) {
    127   DCHECK(thread_checker_.CalledOnValidThread());
    128   DCHECK_EQ(monitor_fd_, fd);
    129 
    130   ScopedUdevDevicePtr device(udev_monitor_receive_device(monitor_.get()));
    131   if (!device)
    132     return;
    133 
    134   std::string action(udev_device_get_action(device.get()));
    135   if (action == kUdevActionAdd)
    136     FOR_EACH_OBSERVER(Observer, observers_, OnDeviceAdded(device.get()));
    137   else if (action == kUdevActionRemove)
    138     FOR_EACH_OBSERVER(Observer, observers_, OnDeviceRemoved(device.get()));
    139 }
    140 
    141 void DeviceMonitorLinux::OnFileCanWriteWithoutBlocking(int fd) {}
    142 
    143 DeviceMonitorLinux::~DeviceMonitorLinux() {
    144   DCHECK(thread_checker_.CalledOnValidThread());
    145   base::MessageLoop::current()->RemoveDestructionObserver(this);
    146   monitor_watcher_.StopWatchingFileDescriptor();
    147   close(monitor_fd_);
    148 }
    149 
    150 }  // namespace device
    151