Commit ba8eca05 authored by Reilly Grant's avatar Reilly Grant Committed by Commit Bot

usb: Support SAMSUNG Mobile USB Composite Device driver

Windows Update installs a custom composite device driver for Samsung
Android devices. This driver appears to behave similarly to Microsoft's
composite device driver except that it does not use the same instance
ID format. This patch adds the driver to the list of recognized
composite drivers and adds fallback logic to find the interface number
in other hardware IDs.

Bug: 1127206
Change-Id: Id48ed6673bf4a195f11a81f8284cc98730e99272
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2436786
Commit-Queue: Reilly Grant <reillyg@chromium.org>
Auto-Submit: Reilly Grant <reillyg@chromium.org>
Reviewed-by: default avatarMatt Reynolds <mattreynolds@chromium.org>
Cr-Commit-Position: refs/heads/master@{#811819}
parent be2eb947
......@@ -29,12 +29,12 @@ UsbDeviceWin::UsbDeviceWin(const std::wstring& device_path,
const base::flat_map<int, FunctionInfo>& functions,
uint32_t bus_number,
uint32_t port_number,
const std::wstring& driver_name)
bool is_supported)
: UsbDevice(bus_number, port_number),
device_path_(device_path),
hub_path_(hub_path),
functions_(functions),
driver_name_(driver_name) {}
is_supported_(is_supported) {}
UsbDeviceWin::~UsbDeviceWin() {}
......@@ -42,16 +42,13 @@ void UsbDeviceWin::Open(OpenCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
scoped_refptr<UsbDeviceHandle> device_handle;
if (base::EqualsCaseInsensitiveASCII(driver_name_, L"winusb") ||
base::EqualsCaseInsensitiveASCII(driver_name_, L"usbccgp")) {
if (is_supported_) {
device_handle = new UsbDeviceHandleWin(this);
}
if (device_handle)
handles().push_back(device_handle.get());
}
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), device_handle));
FROM_HERE, base::BindOnce(std::move(callback), std::move(device_handle)));
}
void UsbDeviceWin::ReadDescriptors(base::OnceCallback<void(bool)> callback) {
......
......@@ -23,6 +23,7 @@ struct WebUsbPlatformCapabilityDescriptor;
class UsbDeviceWin : public UsbDevice {
public:
struct FunctionInfo {
int interface_number;
std::wstring driver;
std::wstring path;
};
......@@ -32,7 +33,7 @@ class UsbDeviceWin : public UsbDevice {
const base::flat_map<int, FunctionInfo>& functions,
uint32_t bus_number,
uint32_t port_number,
const std::wstring& driver_name);
bool is_supported);
// UsbDevice implementation:
void Open(OpenCallback callback) override;
......@@ -47,7 +48,6 @@ class UsbDeviceWin : public UsbDevice {
const base::flat_map<int, FunctionInfo>& functions() const {
return functions_;
}
const std::wstring& driver_name() const { return driver_name_; }
// Opens the device's parent hub in order to read the device, configuration
// and string descriptors.
......@@ -85,7 +85,7 @@ class UsbDeviceWin : public UsbDevice {
const std::wstring device_path_;
const std::wstring hub_path_;
base::flat_map<int, FunctionInfo> functions_;
const std::wstring driver_name_;
const bool is_supported_;
DISALLOW_COPY_AND_ASSIGN(UsbDeviceWin);
};
......
......@@ -41,6 +41,13 @@ 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");
}
base::Optional<uint32_t> GetDeviceUint32Property(HDEVINFO dev_info,
SP_DEVINFO_DATA* dev_info_data,
const DEVPROPKEY& property) {
......@@ -138,6 +145,7 @@ bool GetDeviceInterfaceDetails(HDEVINFO dev_info,
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);
......@@ -230,6 +238,20 @@ bool GetDeviceInterfaceDetails(HDEVINFO dev_info,
*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()) {
......@@ -265,34 +287,50 @@ std::wstring GetDevicePath(const std::wstring& instance_id,
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, /*service_name=*/nullptr)) {
/*child_instance_ids=*/nullptr, /*hardware_ids=*/nullptr,
/*service_name=*/nullptr)) {
return std::wstring();
}
return device_path;
}
int GetInterfaceNumber(const std::wstring& instance_id) {
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 interface_number_str;
if (!RE2::PartialMatch(instance_id_ascii, "MI_([0-9a-fA-F]{2})",
&interface_number_str)) {
return -1;
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(interface_number_str, &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));
......@@ -315,6 +353,16 @@ UsbDeviceWin::FunctionInfo GetFunctionInfo(const std::wstring& instance_id) {
return info;
}
base::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;
......@@ -450,24 +498,27 @@ class UsbServiceWin::BlockingTaskRunnerHelper {
if (!GetDeviceInterfaceDetails(dev_info, device_interface_data,
device_path_ptr, &bus_number, &port_number,
/*instance_id=*/nullptr, &parent_instance_id,
&child_instance_ids, &service_name)) {
&child_instance_ids,
/*hardware_ids=*/nullptr, &service_name)) {
return;
}
bool is_supported = false;
std::vector<std::pair<int, UsbDeviceWin::FunctionInfo>> functions;
if (base::EqualsCaseInsensitiveASCII(service_name, L"usbccgp")) {
// For composite devices Windows loads the usbccgp driver, which creates
// child device nodes for each of the device functions. It is these device
// paths for these children which must be opened in order to communicate
// with the WinUSB driver.
if (IsCompositeDevice(service_name)) {
is_supported = true;
// 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) {
int interface_number = GetInterfaceNumber(instance_id);
if (interface_number != -1) {
functions.emplace_back(interface_number,
GetFunctionInfo(instance_id));
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")) {
is_supported = true;
// A non-composite device has a single device node for all interfaces as
// it only has a single function.
UsbDeviceWin::FunctionInfo info;
......@@ -487,7 +538,7 @@ class UsbServiceWin::BlockingTaskRunnerHelper {
FROM_HERE, base::BindOnce(&UsbServiceWin::CreateDeviceObject, service_,
std::move(device_path), std::move(hub_path),
std::move(functions), bus_number, port_number,
std::move(service_name)));
is_supported, service_name));
}
void EnumeratePotentialFunction(
......@@ -496,16 +547,17 @@ class UsbServiceWin::BlockingTaskRunnerHelper {
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, &service_name)) {
/*child_instance_ids=*/nullptr, &hardware_ids, &service_name)) {
return;
}
int interface_number = GetInterfaceNumber(instance_id);
int interface_number = GetInterfaceNumber(instance_id, hardware_ids);
if (interface_number == -1)
return;
......@@ -608,6 +660,7 @@ void UsbServiceWin::CreateDeviceObject(
const base::flat_map<int, UsbDeviceWin::FunctionInfo>& functions,
uint32_t bus_number,
uint32_t port_number,
bool is_supported,
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
......@@ -616,10 +669,11 @@ void UsbServiceWin::CreateDeviceObject(
++first_enumeration_countdown_;
auto device = base::MakeRefCounted<UsbDeviceWin>(
device_path, hub_path, functions, bus_number, port_number, driver_name);
device_path, hub_path, functions, bus_number, port_number, is_supported);
devices_by_path_[device->device_path()] = device;
device->ReadDescriptors(base::BindOnce(&UsbServiceWin::DeviceReady,
weak_factory_.GetWeakPtr(), device));
weak_factory_.GetWeakPtr(), device,
driver_name));
}
void UsbServiceWin::UpdateFunction(
......@@ -639,6 +693,7 @@ void UsbServiceWin::UpdateFunction(
}
void UsbServiceWin::DeviceReady(scoped_refptr<UsbDeviceWin> device,
const std::wstring& driver_name,
bool success) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
......@@ -664,8 +719,8 @@ void UsbServiceWin::DeviceReady(scoped_refptr<UsbDeviceWin> device,
<< device->manufacturer_string()
<< "\", product=" << device->product_id() << " \""
<< device->product_string() << "\", serial=\""
<< device->serial_number() << "\", driver=\""
<< device->driver_name() << "\", guid=" << device->guid();
<< device->serial_number() << "\", driver=\"" << driver_name
<< "\", guid=" << device->guid();
} else {
devices_by_path_.erase(it);
}
......
......@@ -47,12 +47,15 @@ class UsbServiceWin final : public DeviceMonitorWin::Observer,
const base::flat_map<int, UsbDeviceWin::FunctionInfo>& functions,
uint32_t bus_number,
uint32_t port_number,
bool is_supported,
const std::wstring& driver_name);
void UpdateFunction(const std::wstring& device_path,
int interface_number,
const UsbDeviceWin::FunctionInfo& function_info);
void DeviceReady(scoped_refptr<UsbDeviceWin> device, bool success);
void DeviceReady(scoped_refptr<UsbDeviceWin> device,
const std::wstring& driver_name,
bool success);
bool enumeration_ready() {
return helper_started_ && first_enumeration_countdown_ == 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment