// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "device/hid/device_monitor_linux.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/threading/thread_restrictions.h" #include "device/udev_linux/udev.h" namespace device { namespace { const char kUdevName[] = "udev"; const char kUdevActionAdd[] = "add"; const char kUdevActionRemove[] = "remove"; // The instance will be reset when message loop destroys. base::LazyInstance >::Leaky g_device_monitor_linux_ptr = LAZY_INSTANCE_INITIALIZER; } // namespace DeviceMonitorLinux::DeviceMonitorLinux() : monitor_fd_(-1) { base::ThreadRestrictions::AssertIOAllowed(); base::MessageLoop::current()->AddDestructionObserver(this); udev_.reset(udev_new()); if (!udev_) { LOG(ERROR) << "Failed to create udev."; return; } monitor_.reset(udev_monitor_new_from_netlink(udev_.get(), kUdevName)); if (!monitor_) { LOG(ERROR) << "Failed to create udev monitor."; return; } int ret = udev_monitor_enable_receiving(monitor_.get()); if (ret != 0) { LOG(ERROR) << "Failed to start udev monitoring."; return; } monitor_fd_ = udev_monitor_get_fd(monitor_.get()); if (monitor_fd_ <= 0) { LOG(ERROR) << "Failed to start udev monitoring."; return; } if (!base::MessageLoopForIO::current()->WatchFileDescriptor( monitor_fd_, true, base::MessageLoopForIO::WATCH_READ, &monitor_watcher_, this)) { return; } } // static DeviceMonitorLinux* DeviceMonitorLinux::GetInstance() { if (!HasInstance()) g_device_monitor_linux_ptr.Get().reset(new DeviceMonitorLinux()); return g_device_monitor_linux_ptr.Get().get(); } // static bool DeviceMonitorLinux::HasInstance() { return g_device_monitor_linux_ptr.Get().get(); } void DeviceMonitorLinux::AddObserver(Observer* observer) { DCHECK(thread_checker_.CalledOnValidThread()); if (observer) observers_.AddObserver(observer); } void DeviceMonitorLinux::RemoveObserver(Observer* observer) { DCHECK(thread_checker_.CalledOnValidThread()); if (observer) observers_.RemoveObserver(observer); } ScopedUdevDevicePtr DeviceMonitorLinux::GetDeviceFromPath( const std::string& path) { DCHECK(thread_checker_.CalledOnValidThread()); ScopedUdevDevicePtr device( udev_device_new_from_syspath(udev_.get(), path.c_str())); return device.Pass(); } void DeviceMonitorLinux::Enumerate(const EnumerateCallback& callback) { DCHECK(thread_checker_.CalledOnValidThread()); ScopedUdevEnumeratePtr enumerate(udev_enumerate_new(udev_.get())); if (!enumerate) { LOG(ERROR) << "Failed to enumerate devices."; return; } if (udev_enumerate_scan_devices(enumerate.get()) != 0) { LOG(ERROR) << "Failed to enumerate devices."; return; } // This list is managed by |enumerate|. udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate.get()); for (udev_list_entry* i = devices; i != NULL; i = udev_list_entry_get_next(i)) { ScopedUdevDevicePtr device( udev_device_new_from_syspath(udev_.get(), udev_list_entry_get_name(i))); if (device) callback.Run(device.get()); } } void DeviceMonitorLinux::WillDestroyCurrentMessageLoop() { DCHECK(thread_checker_.CalledOnValidThread()); g_device_monitor_linux_ptr.Get().reset(NULL); } void DeviceMonitorLinux::OnFileCanReadWithoutBlocking(int fd) { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK_EQ(monitor_fd_, fd); ScopedUdevDevicePtr device(udev_monitor_receive_device(monitor_.get())); if (!device) return; std::string action(udev_device_get_action(device.get())); if (action == kUdevActionAdd) FOR_EACH_OBSERVER(Observer, observers_, OnDeviceAdded(device.get())); else if (action == kUdevActionRemove) FOR_EACH_OBSERVER(Observer, observers_, OnDeviceRemoved(device.get())); } void DeviceMonitorLinux::OnFileCanWriteWithoutBlocking(int fd) {} DeviceMonitorLinux::~DeviceMonitorLinux() { DCHECK(thread_checker_.CalledOnValidThread()); base::MessageLoop::current()->RemoveDestructionObserver(this); monitor_watcher_.StopWatchingFileDescriptor(); close(monitor_fd_); } } // namespace device