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