blob: 3b68c55c2be821e64d37e8b4c468fc26d1d6ff5d [file] [log] [blame]
// Copyright 2017 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 "services/device/usb/usb_service_win.h"
#include <objbase.h>
#include <setupapi.h>
#include <stdint.h>
#include <usbiodef.h>
#include "base/strings/string_piece_forward.h"
#define INITGUID
#include <devpkey.h>
#include "base/bind.h"
#include "base/containers/contains.h"
#include "base/location.h"
#include "base/memory/free_deleter.h"
#include "base/memory/ptr_util.h"
#include "base/scoped_generic.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/scoped_blocking_call.h"
#include "base/threading/scoped_thread_priority.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/win/registry.h"
#include "base/win/scoped_devinfo.h"
#include "base/win/scoped_handle.h"
#include "components/device_event_log/device_event_log.h"
#include "services/device/usb/usb_descriptors.h"
#include "services/device/usb/usb_device_handle.h"
#include "services/device/usb/webusb_descriptors.h"
#include "third_party/re2/src/re2/re2.h"
namespace device {
namespace {
bool IsCompositeDevice(const std::wstring& service_name) {
// Windows built-in composite device driver
return base::EqualsCaseInsensitiveASCII(service_name, L"usbccgp") ||
// Samsung Mobile USB Composite device driver
base::EqualsCaseInsensitiveASCII(service_name, L"dg_ssudbus");
}
absl::optional<uint32_t> GetDeviceUint32Property(HDEVINFO dev_info,
SP_DEVINFO_DATA* dev_info_data,
const DEVPROPKEY& property) {
// SetupDiGetDeviceProperty() makes an RPC which may block.
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
DEVPROPTYPE property_type;
uint32_t buffer;
if (!SetupDiGetDeviceProperty(
dev_info, dev_info_data, &property, &property_type,
reinterpret_cast<PBYTE>(&buffer), sizeof(buffer), nullptr, 0) ||
property_type != DEVPROP_TYPE_UINT32) {
return absl::nullopt;
}
return buffer;
}
absl::optional<std::wstring> GetDeviceStringProperty(
HDEVINFO dev_info,
SP_DEVINFO_DATA* dev_info_data,
const DEVPROPKEY& property) {
// SetupDiGetDeviceProperty() makes an RPC which may block.
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
DEVPROPTYPE property_type;
DWORD required_size;
if (SetupDiGetDeviceProperty(dev_info, dev_info_data, &property,
&property_type, nullptr, 0, &required_size, 0) ||
GetLastError() != ERROR_INSUFFICIENT_BUFFER ||
property_type != DEVPROP_TYPE_STRING) {
return absl::nullopt;
}
std::wstring buffer;
if (!SetupDiGetDeviceProperty(
dev_info, dev_info_data, &property, &property_type,
reinterpret_cast<PBYTE>(base::WriteInto(&buffer, required_size)),
required_size, nullptr, 0)) {
return absl::nullopt;
}
return buffer;
}
absl::optional<std::vector<std::wstring>> GetDeviceStringListProperty(
HDEVINFO dev_info,
SP_DEVINFO_DATA* dev_info_data,
const DEVPROPKEY& property) {
// SetupDiGetDeviceProperty() makes an RPC which may block.
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
DEVPROPTYPE property_type;
DWORD required_size;
if (SetupDiGetDeviceProperty(dev_info, dev_info_data, &property,
&property_type, nullptr, 0, &required_size, 0) ||
GetLastError() != ERROR_INSUFFICIENT_BUFFER ||
property_type != DEVPROP_TYPE_STRING_LIST) {
return absl::nullopt;
}
std::wstring buffer;
if (!SetupDiGetDeviceProperty(
dev_info, dev_info_data, &property, &property_type,
reinterpret_cast<PBYTE>(base::WriteInto(&buffer, required_size)),
required_size, nullptr, 0)) {
return absl::nullopt;
}
// Windows string list properties use a NUL character as the delimiter.
return base::SplitString(buffer, base::WStringPiece(L"\0", 1),
base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
}
std::wstring GetServiceName(HDEVINFO dev_info, SP_DEVINFO_DATA* dev_info_data) {
absl::optional<std::wstring> property =
GetDeviceStringProperty(dev_info, dev_info_data, DEVPKEY_Device_Service);
if (!property.has_value())
return std::wstring();
// Windows pads this string with a variable number of NUL bytes for no
// discernible reason.
return std::wstring(base::TrimString(*property, base::WStringPiece(L"\0", 1),
base::TRIM_TRAILING));
}
bool GetDeviceInterfaceDetails(HDEVINFO dev_info,
SP_DEVICE_INTERFACE_DATA* device_interface_data,
std::wstring* device_path,
uint32_t* bus_number,
uint32_t* port_number,
std::wstring* instance_id,
std::wstring* parent_instance_id,
std::vector<std::wstring>* child_instance_ids,
std::vector<std::wstring>* hardware_ids,
std::wstring* service_name) {
SP_DEVINFO_DATA dev_info_data = {};
dev_info_data.cbSize = sizeof(dev_info_data);
DWORD required_size = 0;
std::unique_ptr<SP_DEVICE_INTERFACE_DETAIL_DATA, base::FreeDeleter>
device_interface_detail_data;
// Probing for the required size of the SP_DEVICE_INTERFACE_DETAIL_DATA
// struct is only required if we are looking for the device path.
// Otherwise all the necessary data can be queried from the SP_DEVINFO_DATA.
if (device_path) {
if (!SetupDiGetDeviceInterfaceDetail(dev_info, device_interface_data,
/*DeviceInterfaceDetailData=*/nullptr,
/*DeviceInterfaceDetailDataSize=*/0,
&required_size,
/*DeviceInfoData=*/nullptr) &&
GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
return false;
}
device_interface_detail_data.reset(
static_cast<SP_DEVICE_INTERFACE_DETAIL_DATA*>(malloc(required_size)));
device_interface_detail_data->cbSize =
sizeof(*device_interface_detail_data);
}
if (!SetupDiGetDeviceInterfaceDetail(
dev_info, device_interface_data, device_interface_detail_data.get(),
required_size, /*RequiredSize=*/nullptr, &dev_info_data) &&
(device_path || GetLastError() != ERROR_INSUFFICIENT_BUFFER)) {
USB_PLOG(ERROR) << "SetupDiGetDeviceInterfaceDetail";
return false;
}
if (device_path)
*device_path = std::wstring(device_interface_detail_data->DevicePath);
if (bus_number) {
auto result = GetDeviceUint32Property(dev_info, &dev_info_data,
DEVPKEY_Device_BusNumber);
if (!result.has_value()) {
USB_PLOG(ERROR) << "Failed to get device bus number";
return false;
}
*bus_number = result.value();
}
if (port_number) {
auto result = GetDeviceUint32Property(dev_info, &dev_info_data,
DEVPKEY_Device_Address);
if (!result.has_value()) {
USB_PLOG(ERROR) << "Failed to get device address";
return false;
}
*port_number = result.value();
}
if (instance_id) {
auto result = GetDeviceStringProperty(dev_info, &dev_info_data,
DEVPKEY_Device_InstanceId);
if (!result.has_value()) {
USB_PLOG(ERROR) << "Failed to get the instance ID";
return false;
}
*instance_id = std::move(result.value());
}
if (parent_instance_id) {
auto result = GetDeviceStringProperty(dev_info, &dev_info_data,
DEVPKEY_Device_Parent);
if (!result.has_value()) {
USB_PLOG(ERROR) << "Failed to get the device parent";
return false;
}
*parent_instance_id = std::move(result.value());
}
if (child_instance_ids) {
auto result = GetDeviceStringListProperty(dev_info, &dev_info_data,
DEVPKEY_Device_Children);
if (!result.has_value()) {
if (GetLastError() == ERROR_NOT_FOUND) {
result.emplace();
} else {
USB_PLOG(ERROR) << "Failed to get device children";
return false;
}
}
*child_instance_ids = std::move(result.value());
}
if (hardware_ids) {
auto result = GetDeviceStringListProperty(dev_info, &dev_info_data,
DEVPKEY_Device_HardwareIds);
if (!result.has_value()) {
if (GetLastError() == ERROR_NOT_FOUND) {
result.emplace();
} else {
USB_PLOG(ERROR) << "Failed to get hardware IDs";
return false;
}
}
*hardware_ids = std::move(result.value());
}
if (service_name) {
*service_name = GetServiceName(dev_info, &dev_info_data);
if (service_name->empty()) {
USB_PLOG(ERROR) << "Failed to get device driver name";
return false;
}
}
return true;
}
std::wstring GetDevicePath(const std::wstring& instance_id,
const GUID& device_interface_guid) {
base::win::ScopedDevInfo dev_info(
SetupDiGetClassDevs(&device_interface_guid, instance_id.c_str(), 0,
DIGCF_DEVICEINTERFACE | DIGCF_PRESENT));
if (!dev_info.is_valid()) {
USB_PLOG(ERROR) << "SetupDiGetClassDevs";
return std::wstring();
}
SP_DEVICE_INTERFACE_DATA device_interface_data = {};
device_interface_data.cbSize = sizeof(device_interface_data);
if (!SetupDiEnumDeviceInterfaces(dev_info.get(), nullptr,
&device_interface_guid, 0,
&device_interface_data)) {
USB_PLOG(ERROR) << "SetupDiEnumDeviceInterfaces";
return std::wstring();
}
std::wstring device_path;
if (!GetDeviceInterfaceDetails(
dev_info.get(), &device_interface_data, &device_path,
/*bus_number=*/nullptr, /*port_number=*/nullptr,
/*instance_id=*/nullptr, /*parent_instance_id=*/nullptr,
/*child_instance_ids=*/nullptr, /*hardware_ids=*/nullptr,
/*service_name=*/nullptr)) {
return std::wstring();
}
return device_path;
}
int GetInterfaceNumber(const std::wstring& instance_id,
const std::vector<std::wstring>& hardware_ids) {
// According to MSDN the instance IDs for the device nodes created by the
// composite driver is in the form "USB\VID_vvvv&PID_dddd&MI_zz" where "zz"
// is the interface number.
//
// https://docs.microsoft.com/en-us/windows-hardware/drivers/install/standard-usb-identifiers#multiple-interface-usb-devices
RE2 pattern("MI_([0-9a-fA-F]{2})");
std::string instance_id_ascii = base::WideToASCII(instance_id);
std::string match;
if (!RE2::PartialMatch(instance_id_ascii, pattern, &match)) {
// Alternative composite drivers, such as the one used for Samsung devices,
// don't use the standard format for the instance ID, but one of the
// hardware IDs will still match the expected pattern.
bool found = false;
for (const std::wstring& hardware_id : hardware_ids) {
std::string hardware_id_ascii = base::WideToASCII(hardware_id);
if (RE2::PartialMatch(hardware_id_ascii, pattern, &match)) {
found = true;
break;
}
}
if (!found)
return -1;
}
int interface_number;
if (!base::HexStringToInt(match, &interface_number))
return -1;
return interface_number;
}
UsbDeviceWin::FunctionInfo GetFunctionInfo(const std::wstring& instance_id) {
UsbDeviceWin::FunctionInfo info;
info.interface_number = -1;
base::win::ScopedDevInfo dev_info(
SetupDiCreateDeviceInfoList(nullptr, nullptr));
if (!dev_info.is_valid()) {
USB_PLOG(ERROR) << "SetupDiCreateDeviceInfoList";
return info;
}
SP_DEVINFO_DATA dev_info_data = {};
dev_info_data.cbSize = sizeof(dev_info_data);
if (!SetupDiOpenDeviceInfo(dev_info.get(), instance_id.c_str(), nullptr, 0,
&dev_info_data)) {
USB_PLOG(ERROR) << "SetupDiOpenDeviceInfo";
return info;
}
info.driver = GetServiceName(dev_info.get(), &dev_info_data);
if (info.driver.empty()) {
USB_PLOG(ERROR) << "Could not get child device's service name";
return info;
}
absl::optional<std::vector<std::wstring>> hardware_ids =
GetDeviceStringListProperty(dev_info.get(), &dev_info_data,
DEVPKEY_Device_HardwareIds);
if (!hardware_ids) {
USB_PLOG(ERROR) << "Could not get the child device's hardware IDs";
return info;
}
info.interface_number = GetInterfaceNumber(instance_id, *hardware_ids);
if (!base::EqualsCaseInsensitiveASCII(info.driver, L"winusb"))
return info;
// Boost priority while potentially loading Advapi32.dll on a background
// thread for the registry functions used below.
SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
// There is no standard device interface GUID for USB functions and so we
// must discover the set of GUIDs that have been set in the registry by
// the INF file or Microsoft OS Compatibility descriptors before
// SetupDiGetDeviceInterfaceDetail() can be used to get the device path.
HKEY key = SetupDiOpenDevRegKey(dev_info.get(), &dev_info_data,
DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
if (key == INVALID_HANDLE_VALUE) {
USB_PLOG(ERROR) << "Could not open device registry key";
return info;
}
base::win::RegKey scoped_key(key);
std::vector<std::wstring> device_interface_guids;
LONG result =
scoped_key.ReadValues(L"DeviceInterfaceGUIDs", &device_interface_guids);
if (result != ERROR_SUCCESS) {
USB_LOG(ERROR) << "Could not read device interface GUIDs: "
<< logging::SystemErrorCodeToString(result);
return info;
}
for (const auto& guid_string : device_interface_guids) {
// Boost priority while potentially loading Ole32.dll on a background
// thread for CLSIDFromString().
SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
GUID guid;
if (FAILED(CLSIDFromString(guid_string.c_str(), &guid))) {
USB_LOG(ERROR) << "Failed to parse device interface GUID: "
<< guid_string;
continue;
}
info.path = GetDevicePath(instance_id, guid);
if (!info.path.empty())
return info;
}
return info;
}
} // namespace
class UsbServiceWin::BlockingTaskRunnerHelper {
public:
explicit BlockingTaskRunnerHelper(base::WeakPtr<UsbServiceWin> service)
: service_task_runner_(base::ThreadTaskRunnerHandle::Get()),
service_(service) {}
~BlockingTaskRunnerHelper() {}
void EnumerateDevices() {
// Boost priority while potentially loading SetupAPI.dll for the following
// functions on a background thread.
SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
base::win::ScopedDevInfo dev_info(
SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE, nullptr, 0,
DIGCF_DEVICEINTERFACE | DIGCF_PRESENT));
if (!dev_info.is_valid()) {
USB_PLOG(ERROR) << "Failed to set up device enumeration";
service_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&UsbServiceWin::HelperStarted, service_));
return;
}
SP_DEVICE_INTERFACE_DATA device_interface_data = {};
device_interface_data.cbSize = sizeof(device_interface_data);
for (DWORD i = 0; SetupDiEnumDeviceInterfaces(dev_info.get(), nullptr,
&GUID_DEVINTERFACE_USB_DEVICE,
i, &device_interface_data);
++i) {
EnumerateDevice(dev_info.get(), &device_interface_data, absl::nullopt);
}
if (GetLastError() != ERROR_NO_MORE_ITEMS)
USB_PLOG(ERROR) << "Failed to enumerate devices";
service_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&UsbServiceWin::HelperStarted, service_));
}
void OnDeviceAdded(const GUID& guid, const std::wstring& device_path) {
// Boost priority while potentially loading SetupAPI.dll and Ole32.dll on a
// background thread for the following functions.
SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
base::win::ScopedDevInfo dev_info(SetupDiGetClassDevs(
&guid, nullptr, 0, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT));
if (!dev_info.is_valid()) {
USB_PLOG(ERROR) << "Failed to set up device enumeration";
return;
}
SP_DEVICE_INTERFACE_DATA device_interface_data = {};
device_interface_data.cbSize = sizeof(device_interface_data);
if (!SetupDiOpenDeviceInterface(dev_info.get(), device_path.c_str(), 0,
&device_interface_data)) {
USB_PLOG(ERROR) << "Failed to add device interface: " << device_path;
return;
}
if (IsEqualGUID(guid, GUID_DEVINTERFACE_USB_DEVICE)) {
EnumerateDevice(dev_info.get(), &device_interface_data, device_path);
} else {
EnumeratePotentialFunction(dev_info.get(), &device_interface_data,
device_path);
}
}
private:
void EnumerateDevice(HDEVINFO dev_info,
SP_DEVICE_INTERFACE_DATA* device_interface_data,
const absl::optional<std::wstring>& opt_device_path) {
std::wstring device_path;
std::wstring* device_path_ptr = &device_path;
if (opt_device_path) {
device_path = *opt_device_path;
device_path_ptr = nullptr;
}
uint32_t bus_number;
uint32_t port_number;
std::wstring parent_instance_id;
std::vector<std::wstring> child_instance_ids;
std::wstring service_name;
if (!GetDeviceInterfaceDetails(dev_info, device_interface_data,
device_path_ptr, &bus_number, &port_number,
/*instance_id=*/nullptr, &parent_instance_id,
&child_instance_ids,
/*hardware_ids=*/nullptr, &service_name)) {
return;
}
auto driver_type = UsbDeviceWin::DriverType::kUnsupported;
std::vector<std::pair<int, UsbDeviceWin::FunctionInfo>> functions;
if (IsCompositeDevice(service_name)) {
driver_type = UsbDeviceWin::DriverType::kComposite;
// For composite devices Windows a composite device driver (usually the
// built-in usbccgp.sys) creates child device nodes for each device
// function. The device paths for these children must be opened in order
// to communicate with the WinUSB driver.
for (const std::wstring& instance_id : child_instance_ids) {
UsbDeviceWin::FunctionInfo info = GetFunctionInfo(instance_id);
if (info.interface_number != -1) {
functions.emplace_back(info.interface_number, info);
}
}
} else if (base::EqualsCaseInsensitiveASCII(service_name, L"winusb")) {
driver_type = UsbDeviceWin::DriverType::kWinUSB;
// A non-composite device has a single device node for all interfaces. It
// may still include multiple functions but they will be ignored.
UsbDeviceWin::FunctionInfo info;
info.driver = service_name;
info.path = device_path;
functions.emplace_back(/*interface_number=*/0, info);
}
std::wstring& hub_path = hub_paths_[parent_instance_id];
if (hub_path.empty()) {
hub_path = GetDevicePath(parent_instance_id, GUID_DEVINTERFACE_USB_HUB);
if (hub_path.empty())
return;
}
service_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&UsbServiceWin::CreateDeviceObject, service_,
std::move(device_path), std::move(hub_path),
std::move(functions), bus_number, port_number,
driver_type, service_name));
}
void EnumeratePotentialFunction(
HDEVINFO dev_info,
SP_DEVICE_INTERFACE_DATA* device_interface_data,
const std::wstring& device_path) {
std::wstring instance_id;
std::wstring parent_instance_id;
std::vector<std::wstring> hardware_ids;
std::wstring service_name;
if (!GetDeviceInterfaceDetails(
dev_info, device_interface_data,
/*device_path=*/nullptr, /*bus_number=*/nullptr,
/*port_number=*/nullptr, &instance_id, &parent_instance_id,
/*child_instance_ids=*/nullptr, &hardware_ids, &service_name)) {
return;
}
int interface_number = GetInterfaceNumber(instance_id, hardware_ids);
if (interface_number == -1)
return;
std::wstring parent_path =
GetDevicePath(parent_instance_id, GUID_DEVINTERFACE_USB_DEVICE);
if (parent_path.empty())
return;
UsbDeviceWin::FunctionInfo info;
info.driver = service_name;
info.path = device_path;
service_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&UsbServiceWin::UpdateFunction, service_,
std::move(parent_path), interface_number, info));
}
std::unordered_map<std::wstring, std::wstring> hub_paths_;
// Calls back to |service_| must be posted to |service_task_runner_|, which
// runs tasks on the thread where that object lives.
scoped_refptr<base::SingleThreadTaskRunner> service_task_runner_;
base::WeakPtr<UsbServiceWin> service_;
};
UsbServiceWin::UsbServiceWin()
: UsbService(),
blocking_task_runner_(CreateBlockingTaskRunner()),
helper_(nullptr, base::OnTaskRunnerDeleter(blocking_task_runner_)) {
DeviceMonitorWin* device_monitor = DeviceMonitorWin::GetForAllInterfaces();
if (device_monitor)
device_observation_.Observe(device_monitor);
helper_.reset(new BlockingTaskRunnerHelper(weak_factory_.GetWeakPtr()));
blocking_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&BlockingTaskRunnerHelper::EnumerateDevices,
base::Unretained(helper_.get())));
}
UsbServiceWin::~UsbServiceWin() {
NotifyWillDestroyUsbService();
}
void UsbServiceWin::GetDevices(GetDevicesCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (enumeration_ready())
UsbService::GetDevices(std::move(callback));
else
enumeration_callbacks_.push_back(std::move(callback));
}
void UsbServiceWin::OnDeviceAdded(const GUID& class_guid,
const std::wstring& device_path) {
blocking_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&BlockingTaskRunnerHelper::OnDeviceAdded,
base::Unretained(helper_.get()), class_guid, device_path));
}
void UsbServiceWin::OnDeviceRemoved(const GUID& class_guid,
const std::wstring& device_path) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto by_path_it = devices_by_path_.find(device_path);
if (by_path_it == devices_by_path_.end())
return;
scoped_refptr<UsbDeviceWin> device = by_path_it->second;
devices_by_path_.erase(by_path_it);
device->OnDisconnect();
auto by_guid_it = devices().find(device->guid());
if (by_guid_it != devices().end() && enumeration_ready()) {
USB_LOG(USER) << "USB device removed: path=" << device->device_path()
<< " guid=" << device->guid();
devices().erase(by_guid_it);
NotifyDeviceRemoved(device);
}
}
void UsbServiceWin::HelperStarted() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
helper_started_ = true;
if (enumeration_ready()) {
std::vector<scoped_refptr<UsbDevice>> result;
result.reserve(devices().size());
for (const auto& map_entry : devices())
result.push_back(map_entry.second);
for (auto& callback : enumeration_callbacks_)
std::move(callback).Run(result);
enumeration_callbacks_.clear();
}
}
void UsbServiceWin::CreateDeviceObject(
const std::wstring& device_path,
const std::wstring& hub_path,
const base::flat_map<int, UsbDeviceWin::FunctionInfo>& functions,
uint32_t bus_number,
uint32_t port_number,
UsbDeviceWin::DriverType driver_type,
const std::wstring& driver_name) {
// Devices that appear during initial enumeration are gathered into the first
// result returned by GetDevices() and prevent device add/remove notifications
// from being sent.
if (!enumeration_ready())
++first_enumeration_countdown_;
auto device = base::MakeRefCounted<UsbDeviceWin>(
device_path, hub_path, functions, bus_number, port_number, driver_type);
devices_by_path_[device->device_path()] = device;
device->ReadDescriptors(base::BindOnce(&UsbServiceWin::DeviceReady,
weak_factory_.GetWeakPtr(), device,
driver_name));
}
void UsbServiceWin::UpdateFunction(
const std::wstring& device_path,
int interface_number,
const UsbDeviceWin::FunctionInfo& function_info) {
auto it = devices_by_path_.find(device_path);
if (it == devices_by_path_.end())
return;
const scoped_refptr<UsbDeviceWin>& device = it->second;
USB_LOG(EVENT) << "USB device function updated: guid=" << device->guid()
<< ", interface_number=" << interface_number << ", path=\""
<< function_info.path << "\", driver=\""
<< function_info.driver << "\"";
device->UpdateFunction(interface_number, function_info);
}
void UsbServiceWin::DeviceReady(scoped_refptr<UsbDeviceWin> device,
const std::wstring& driver_name,
bool success) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
bool enumeration_became_ready = false;
if (!enumeration_ready()) {
DCHECK_GT(first_enumeration_countdown_, 0u);
first_enumeration_countdown_--;
if (enumeration_ready())
enumeration_became_ready = true;
}
// If |device| was disconnected while descriptors were being read then it
// will have been removed from |devices_by_path_|.
auto it = devices_by_path_.find(device->device_path());
if (it == devices_by_path_.end()) {
success = false;
} else if (success) {
DCHECK(!base::Contains(devices(), device->guid()));
devices()[device->guid()] = device;
USB_LOG(USER) << "USB device added: path=" << device->device_path()
<< " vendor=" << device->vendor_id() << " \""
<< device->manufacturer_string()
<< "\", product=" << device->product_id() << " \""
<< device->product_string() << "\", serial=\""
<< device->serial_number() << "\", driver=\"" << driver_name
<< "\", guid=" << device->guid();
} else {
devices_by_path_.erase(it);
}
if (enumeration_became_ready) {
std::vector<scoped_refptr<UsbDevice>> result;
result.reserve(devices().size());
for (const auto& map_entry : devices())
result.push_back(map_entry.second);
for (auto& callback : enumeration_callbacks_)
std::move(callback).Run(result);
enumeration_callbacks_.clear();
} else if (success && enumeration_ready()) {
NotifyDeviceAdded(device);
}
}
} // namespace device