| // Copyright (c) 2013 The Chromium OS 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 "mist/usb_device_event_notifier.h" |
| |
| #include <limits> |
| |
| #include <base/bind.h> |
| #include <base/bind_helpers.h> |
| #include <base/logging.h> |
| #include <base/strings/string_number_conversions.h> |
| #include <base/strings/stringprintf.h> |
| #include <brillo/udev/udev.h> |
| #include <brillo/udev/udev_device.h> |
| #include <brillo/udev/udev_enumerate.h> |
| #include <brillo/udev/udev_monitor.h> |
| |
| #include "mist/usb_device_event_observer.h" |
| |
| namespace mist { |
| |
| namespace { |
| |
| const char kAttributeBusNumber[] = "busnum"; |
| const char kAttributeDeviceAddress[] = "devnum"; |
| const char kAttributeIdProduct[] = "idProduct"; |
| const char kAttributeIdVendor[] = "idVendor"; |
| |
| } // namespace |
| |
| UsbDeviceEventNotifier::UsbDeviceEventNotifier(brillo::Udev* udev) |
| : udev_(udev) { |
| CHECK(udev_); |
| } |
| |
| UsbDeviceEventNotifier::~UsbDeviceEventNotifier() = default; |
| |
| bool UsbDeviceEventNotifier::Initialize() { |
| udev_monitor_ = udev_->CreateMonitorFromNetlink("udev"); |
| if (!udev_monitor_) { |
| LOG(ERROR) << "Could not create udev monitor."; |
| return false; |
| } |
| |
| if (!udev_monitor_->FilterAddMatchSubsystemDeviceType("usb", "usb_device")) { |
| LOG(ERROR) << "Could not add udev monitor filter."; |
| return false; |
| } |
| |
| if (!udev_monitor_->EnableReceiving()) { |
| LOG(ERROR) << "Could not enable udev monitoring."; |
| return false; |
| } |
| |
| int udev_monitor_fd = udev_monitor_->GetFileDescriptor(); |
| if (udev_monitor_fd == brillo::UdevMonitor::kInvalidFileDescriptor) { |
| LOG(ERROR) << "Could not get udev monitor file descriptor."; |
| return false; |
| } |
| |
| udev_monitor_watcher_ = base::FileDescriptorWatcher::WatchReadable( |
| udev_monitor_fd, |
| base::BindRepeating( |
| &UsbDeviceEventNotifier::OnUdevMonitorFileDescriptorReadable, |
| base::Unretained(this))); |
| if (!udev_monitor_watcher_) { |
| LOG(ERROR) << "Could not watch udev monitor file descriptor."; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool UsbDeviceEventNotifier::ScanExistingDevices() { |
| std::unique_ptr<brillo::UdevEnumerate> enumerate = udev_->CreateEnumerate(); |
| if (!enumerate || !enumerate->AddMatchSubsystem("usb") || |
| !enumerate->AddMatchProperty("DEVTYPE", "usb_device") || |
| !enumerate->ScanDevices()) { |
| LOG(ERROR) << "Could not enumerate USB devices on the system."; |
| return false; |
| } |
| |
| for (std::unique_ptr<brillo::UdevListEntry> list_entry = |
| enumerate->GetListEntry(); |
| list_entry; list_entry = list_entry->GetNext()) { |
| std::string sys_path = ConvertNullToEmptyString(list_entry->GetName()); |
| |
| std::unique_ptr<brillo::UdevDevice> device = |
| udev_->CreateDeviceFromSysPath(sys_path.c_str()); |
| if (!device) |
| continue; |
| |
| uint8_t bus_number; |
| uint8_t device_address; |
| uint16_t vendor_id; |
| uint16_t product_id; |
| if (!GetDeviceAttributes(device.get(), &bus_number, &device_address, |
| &vendor_id, &product_id)) |
| continue; |
| |
| for (UsbDeviceEventObserver& observer : observer_list_) { |
| observer.OnUsbDeviceAdded(sys_path, bus_number, device_address, vendor_id, |
| product_id); |
| } |
| } |
| return true; |
| } |
| |
| void UsbDeviceEventNotifier::AddObserver(UsbDeviceEventObserver* observer) { |
| CHECK(observer); |
| |
| observer_list_.AddObserver(observer); |
| } |
| |
| void UsbDeviceEventNotifier::RemoveObserver(UsbDeviceEventObserver* observer) { |
| CHECK(observer); |
| |
| observer_list_.RemoveObserver(observer); |
| } |
| |
| void UsbDeviceEventNotifier::OnUdevMonitorFileDescriptorReadable() { |
| VLOG(3) << "Udev file descriptor available for read."; |
| |
| std::unique_ptr<brillo::UdevDevice> device = udev_monitor_->ReceiveDevice(); |
| if (!device) { |
| LOG(WARNING) << "Ignore device event with no associated udev device."; |
| return; |
| } |
| |
| VLOG(1) << base::StringPrintf( |
| "udev (SysPath=%s, " |
| "Node=%s, " |
| "Subsystem=%s, " |
| "DevType=%s, " |
| "Action=%s, " |
| "BusNumber=%s, " |
| "DeviceAddress=%s, " |
| "VendorId=%s, " |
| "ProductId=%s)", |
| device->GetSysPath(), device->GetDeviceNode(), device->GetSubsystem(), |
| device->GetDeviceType(), device->GetAction(), |
| device->GetSysAttributeValue(kAttributeBusNumber), |
| device->GetSysAttributeValue(kAttributeDeviceAddress), |
| device->GetSysAttributeValue(kAttributeIdVendor), |
| device->GetSysAttributeValue(kAttributeIdProduct)); |
| |
| std::string sys_path = ConvertNullToEmptyString(device->GetSysPath()); |
| if (sys_path.empty()) { |
| LOG(WARNING) << "Ignore device event with no device sysfs path."; |
| return; |
| } |
| |
| std::string action = ConvertNullToEmptyString(device->GetAction()); |
| if (action == "add") { |
| uint8_t bus_number; |
| uint8_t device_address; |
| uint16_t vendor_id; |
| uint16_t product_id; |
| if (!GetDeviceAttributes(device.get(), &bus_number, &device_address, |
| &vendor_id, &product_id)) { |
| LOG(WARNING) << "Ignore device event of unidentifiable device."; |
| return; |
| } |
| |
| for (UsbDeviceEventObserver& observer : observer_list_) { |
| observer.OnUsbDeviceAdded(sys_path, bus_number, device_address, vendor_id, |
| product_id); |
| } |
| return; |
| } |
| |
| if (action == "remove") { |
| for (UsbDeviceEventObserver& observer : observer_list_) |
| observer.OnUsbDeviceRemoved(sys_path); |
| } |
| } |
| |
| // static |
| std::string UsbDeviceEventNotifier::ConvertNullToEmptyString(const char* str) { |
| return str ? str : std::string(); |
| } |
| |
| // static |
| bool UsbDeviceEventNotifier::ConvertHexStringToUint16(const std::string& str, |
| uint16_t* value) { |
| int temp_value = -1; |
| if (str.size() != 4 || !base::HexStringToInt(str, &temp_value) || |
| temp_value < 0 || temp_value > std::numeric_limits<uint16_t>::max()) { |
| return false; |
| } |
| |
| *value = static_cast<uint16_t>(temp_value); |
| return true; |
| } |
| |
| // static |
| bool UsbDeviceEventNotifier::ConvertStringToUint8(const std::string& str, |
| uint8_t* value) { |
| unsigned temp_value = 0; |
| if (!base::StringToUint(str, &temp_value) || |
| temp_value > std::numeric_limits<uint8_t>::max()) { |
| return false; |
| } |
| |
| *value = static_cast<uint8_t>(temp_value); |
| return true; |
| } |
| |
| // static |
| bool UsbDeviceEventNotifier::GetDeviceAttributes( |
| const brillo::UdevDevice* device, |
| uint8_t* bus_number, |
| uint8_t* device_address, |
| uint16_t* vendor_id, |
| uint16_t* product_id) { |
| std::string bus_number_string = ConvertNullToEmptyString( |
| device->GetSysAttributeValue(kAttributeBusNumber)); |
| if (!ConvertStringToUint8(bus_number_string, bus_number)) { |
| LOG(WARNING) << "Invalid USB bus number '" << bus_number_string << "'."; |
| return false; |
| } |
| |
| std::string device_address_string = ConvertNullToEmptyString( |
| device->GetSysAttributeValue(kAttributeDeviceAddress)); |
| if (!ConvertStringToUint8(device_address_string, device_address)) { |
| LOG(WARNING) << "Invalid USB device address '" << device_address_string |
| << "'."; |
| return false; |
| } |
| |
| std::string vendor_id_string = ConvertNullToEmptyString( |
| device->GetSysAttributeValue(kAttributeIdVendor)); |
| if (!ConvertHexStringToUint16(vendor_id_string, vendor_id)) { |
| LOG(WARNING) << "Invalid USB vendor ID '" << vendor_id_string << "'."; |
| return false; |
| } |
| |
| std::string product_id_string = ConvertNullToEmptyString( |
| device->GetSysAttributeValue(kAttributeIdProduct)); |
| if (!ConvertHexStringToUint16(product_id_string, product_id)) { |
| LOG(WARNING) << "Invalid USB product ID '" << product_id_string << "'."; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| } // namespace mist |