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, ...@@ -29,12 +29,12 @@ UsbDeviceWin::UsbDeviceWin(const std::wstring& device_path,
const base::flat_map<int, FunctionInfo>& functions, const base::flat_map<int, FunctionInfo>& functions,
uint32_t bus_number, uint32_t bus_number,
uint32_t port_number, uint32_t port_number,
const std::wstring& driver_name) bool is_supported)
: UsbDevice(bus_number, port_number), : UsbDevice(bus_number, port_number),
device_path_(device_path), device_path_(device_path),
hub_path_(hub_path), hub_path_(hub_path),
functions_(functions), functions_(functions),
driver_name_(driver_name) {} is_supported_(is_supported) {}
UsbDeviceWin::~UsbDeviceWin() {} UsbDeviceWin::~UsbDeviceWin() {}
...@@ -42,16 +42,13 @@ void UsbDeviceWin::Open(OpenCallback callback) { ...@@ -42,16 +42,13 @@ void UsbDeviceWin::Open(OpenCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
scoped_refptr<UsbDeviceHandle> device_handle; scoped_refptr<UsbDeviceHandle> device_handle;
if (base::EqualsCaseInsensitiveASCII(driver_name_, L"winusb") || if (is_supported_) {
base::EqualsCaseInsensitiveASCII(driver_name_, L"usbccgp")) {
device_handle = new UsbDeviceHandleWin(this); device_handle = new UsbDeviceHandleWin(this);
}
if (device_handle)
handles().push_back(device_handle.get()); handles().push_back(device_handle.get());
}
base::SequencedTaskRunnerHandle::Get()->PostTask( 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) { void UsbDeviceWin::ReadDescriptors(base::OnceCallback<void(bool)> callback) {
......
...@@ -23,6 +23,7 @@ struct WebUsbPlatformCapabilityDescriptor; ...@@ -23,6 +23,7 @@ struct WebUsbPlatformCapabilityDescriptor;
class UsbDeviceWin : public UsbDevice { class UsbDeviceWin : public UsbDevice {
public: public:
struct FunctionInfo { struct FunctionInfo {
int interface_number;
std::wstring driver; std::wstring driver;
std::wstring path; std::wstring path;
}; };
...@@ -32,7 +33,7 @@ class UsbDeviceWin : public UsbDevice { ...@@ -32,7 +33,7 @@ class UsbDeviceWin : public UsbDevice {
const base::flat_map<int, FunctionInfo>& functions, const base::flat_map<int, FunctionInfo>& functions,
uint32_t bus_number, uint32_t bus_number,
uint32_t port_number, uint32_t port_number,
const std::wstring& driver_name); bool is_supported);
// UsbDevice implementation: // UsbDevice implementation:
void Open(OpenCallback callback) override; void Open(OpenCallback callback) override;
...@@ -47,7 +48,6 @@ class UsbDeviceWin : public UsbDevice { ...@@ -47,7 +48,6 @@ class UsbDeviceWin : public UsbDevice {
const base::flat_map<int, FunctionInfo>& functions() const { const base::flat_map<int, FunctionInfo>& functions() const {
return functions_; return functions_;
} }
const std::wstring& driver_name() const { return driver_name_; }
// Opens the device's parent hub in order to read the device, configuration // Opens the device's parent hub in order to read the device, configuration
// and string descriptors. // and string descriptors.
...@@ -85,7 +85,7 @@ class UsbDeviceWin : public UsbDevice { ...@@ -85,7 +85,7 @@ class UsbDeviceWin : public UsbDevice {
const std::wstring device_path_; const std::wstring device_path_;
const std::wstring hub_path_; const std::wstring hub_path_;
base::flat_map<int, FunctionInfo> functions_; base::flat_map<int, FunctionInfo> functions_;
const std::wstring driver_name_; const bool is_supported_;
DISALLOW_COPY_AND_ASSIGN(UsbDeviceWin); DISALLOW_COPY_AND_ASSIGN(UsbDeviceWin);
}; };
......
...@@ -41,6 +41,13 @@ namespace device { ...@@ -41,6 +41,13 @@ namespace device {
namespace { 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, base::Optional<uint32_t> GetDeviceUint32Property(HDEVINFO dev_info,
SP_DEVINFO_DATA* dev_info_data, SP_DEVINFO_DATA* dev_info_data,
const DEVPROPKEY& property) { const DEVPROPKEY& property) {
...@@ -138,6 +145,7 @@ bool GetDeviceInterfaceDetails(HDEVINFO dev_info, ...@@ -138,6 +145,7 @@ bool GetDeviceInterfaceDetails(HDEVINFO dev_info,
std::wstring* instance_id, std::wstring* instance_id,
std::wstring* parent_instance_id, std::wstring* parent_instance_id,
std::vector<std::wstring>* child_instance_ids, std::vector<std::wstring>* child_instance_ids,
std::vector<std::wstring>* hardware_ids,
std::wstring* service_name) { std::wstring* service_name) {
SP_DEVINFO_DATA dev_info_data = {}; SP_DEVINFO_DATA dev_info_data = {};
dev_info_data.cbSize = sizeof(dev_info_data); dev_info_data.cbSize = sizeof(dev_info_data);
...@@ -230,6 +238,20 @@ bool GetDeviceInterfaceDetails(HDEVINFO dev_info, ...@@ -230,6 +238,20 @@ bool GetDeviceInterfaceDetails(HDEVINFO dev_info,
*child_instance_ids = std::move(result.value()); *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) { if (service_name) {
*service_name = GetServiceName(dev_info, &dev_info_data); *service_name = GetServiceName(dev_info, &dev_info_data);
if (service_name->empty()) { if (service_name->empty()) {
...@@ -265,34 +287,50 @@ std::wstring GetDevicePath(const std::wstring& instance_id, ...@@ -265,34 +287,50 @@ std::wstring GetDevicePath(const std::wstring& instance_id,
dev_info.get(), &device_interface_data, &device_path, dev_info.get(), &device_interface_data, &device_path,
/*bus_number=*/nullptr, /*port_number=*/nullptr, /*bus_number=*/nullptr, /*port_number=*/nullptr,
/*instance_id=*/nullptr, /*parent_instance_id=*/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 std::wstring();
} }
return device_path; 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 // 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" // composite driver is in the form "USB\VID_vvvv&PID_dddd&MI_zz" where "zz"
// is the interface number. // is the interface number.
// //
// https://docs.microsoft.com/en-us/windows-hardware/drivers/install/standard-usb-identifiers#multiple-interface-usb-devices // 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 instance_id_ascii = base::WideToASCII(instance_id);
std::string interface_number_str; std::string match;
if (!RE2::PartialMatch(instance_id_ascii, "MI_([0-9a-fA-F]{2})", if (!RE2::PartialMatch(instance_id_ascii, pattern, &match)) {
&interface_number_str)) { // Alternative composite drivers, such as the one used for Samsung devices,
return -1; // 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; int interface_number;
if (!base::HexStringToInt(interface_number_str, &interface_number)) if (!base::HexStringToInt(match, &interface_number))
return -1; return -1;
return interface_number; return interface_number;
} }
UsbDeviceWin::FunctionInfo GetFunctionInfo(const std::wstring& instance_id) { UsbDeviceWin::FunctionInfo GetFunctionInfo(const std::wstring& instance_id) {
UsbDeviceWin::FunctionInfo info; UsbDeviceWin::FunctionInfo info;
info.interface_number = -1;
base::win::ScopedDevInfo dev_info( base::win::ScopedDevInfo dev_info(
SetupDiCreateDeviceInfoList(nullptr, nullptr)); SetupDiCreateDeviceInfoList(nullptr, nullptr));
...@@ -315,6 +353,16 @@ UsbDeviceWin::FunctionInfo GetFunctionInfo(const std::wstring& instance_id) { ...@@ -315,6 +353,16 @@ UsbDeviceWin::FunctionInfo GetFunctionInfo(const std::wstring& instance_id) {
return info; 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")) if (!base::EqualsCaseInsensitiveASCII(info.driver, L"winusb"))
return info; return info;
...@@ -450,24 +498,27 @@ class UsbServiceWin::BlockingTaskRunnerHelper { ...@@ -450,24 +498,27 @@ class UsbServiceWin::BlockingTaskRunnerHelper {
if (!GetDeviceInterfaceDetails(dev_info, device_interface_data, if (!GetDeviceInterfaceDetails(dev_info, device_interface_data,
device_path_ptr, &bus_number, &port_number, device_path_ptr, &bus_number, &port_number,
/*instance_id=*/nullptr, &parent_instance_id, /*instance_id=*/nullptr, &parent_instance_id,
&child_instance_ids, &service_name)) { &child_instance_ids,
/*hardware_ids=*/nullptr, &service_name)) {
return; return;
} }
bool is_supported = false;
std::vector<std::pair<int, UsbDeviceWin::FunctionInfo>> functions; std::vector<std::pair<int, UsbDeviceWin::FunctionInfo>> functions;
if (base::EqualsCaseInsensitiveASCII(service_name, L"usbccgp")) { if (IsCompositeDevice(service_name)) {
// For composite devices Windows loads the usbccgp driver, which creates is_supported = true;
// child device nodes for each of the device functions. It is these device // For composite devices Windows a composite device driver (usually the
// paths for these children which must be opened in order to communicate // built-in usbccgp.sys) creates child device nodes for each device
// with the WinUSB driver. // 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) { for (const std::wstring& instance_id : child_instance_ids) {
int interface_number = GetInterfaceNumber(instance_id); UsbDeviceWin::FunctionInfo info = GetFunctionInfo(instance_id);
if (interface_number != -1) { if (info.interface_number != -1) {
functions.emplace_back(interface_number, functions.emplace_back(info.interface_number, info);
GetFunctionInfo(instance_id));
} }
} }
} else if (base::EqualsCaseInsensitiveASCII(service_name, L"winusb")) { } else if (base::EqualsCaseInsensitiveASCII(service_name, L"winusb")) {
is_supported = true;
// A non-composite device has a single device node for all interfaces as // A non-composite device has a single device node for all interfaces as
// it only has a single function. // it only has a single function.
UsbDeviceWin::FunctionInfo info; UsbDeviceWin::FunctionInfo info;
...@@ -487,7 +538,7 @@ class UsbServiceWin::BlockingTaskRunnerHelper { ...@@ -487,7 +538,7 @@ class UsbServiceWin::BlockingTaskRunnerHelper {
FROM_HERE, base::BindOnce(&UsbServiceWin::CreateDeviceObject, service_, FROM_HERE, base::BindOnce(&UsbServiceWin::CreateDeviceObject, service_,
std::move(device_path), std::move(hub_path), std::move(device_path), std::move(hub_path),
std::move(functions), bus_number, port_number, std::move(functions), bus_number, port_number,
std::move(service_name))); is_supported, service_name));
} }
void EnumeratePotentialFunction( void EnumeratePotentialFunction(
...@@ -496,16 +547,17 @@ class UsbServiceWin::BlockingTaskRunnerHelper { ...@@ -496,16 +547,17 @@ class UsbServiceWin::BlockingTaskRunnerHelper {
const std::wstring& device_path) { const std::wstring& device_path) {
std::wstring instance_id; std::wstring instance_id;
std::wstring parent_instance_id; std::wstring parent_instance_id;
std::vector<std::wstring> hardware_ids;
std::wstring service_name; std::wstring service_name;
if (!GetDeviceInterfaceDetails( if (!GetDeviceInterfaceDetails(
dev_info, device_interface_data, dev_info, device_interface_data,
/*device_path=*/nullptr, /*bus_number=*/nullptr, /*device_path=*/nullptr, /*bus_number=*/nullptr,
/*port_number=*/nullptr, &instance_id, &parent_instance_id, /*port_number=*/nullptr, &instance_id, &parent_instance_id,
/*child_instance_ids=*/nullptr, &service_name)) { /*child_instance_ids=*/nullptr, &hardware_ids, &service_name)) {
return; return;
} }
int interface_number = GetInterfaceNumber(instance_id); int interface_number = GetInterfaceNumber(instance_id, hardware_ids);
if (interface_number == -1) if (interface_number == -1)
return; return;
...@@ -608,6 +660,7 @@ void UsbServiceWin::CreateDeviceObject( ...@@ -608,6 +660,7 @@ void UsbServiceWin::CreateDeviceObject(
const base::flat_map<int, UsbDeviceWin::FunctionInfo>& functions, const base::flat_map<int, UsbDeviceWin::FunctionInfo>& functions,
uint32_t bus_number, uint32_t bus_number,
uint32_t port_number, uint32_t port_number,
bool is_supported,
const std::wstring& driver_name) { const std::wstring& driver_name) {
// Devices that appear during initial enumeration are gathered into the first // Devices that appear during initial enumeration are gathered into the first
// result returned by GetDevices() and prevent device add/remove notifications // result returned by GetDevices() and prevent device add/remove notifications
...@@ -616,10 +669,11 @@ void UsbServiceWin::CreateDeviceObject( ...@@ -616,10 +669,11 @@ void UsbServiceWin::CreateDeviceObject(
++first_enumeration_countdown_; ++first_enumeration_countdown_;
auto device = base::MakeRefCounted<UsbDeviceWin>( 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; devices_by_path_[device->device_path()] = device;
device->ReadDescriptors(base::BindOnce(&UsbServiceWin::DeviceReady, device->ReadDescriptors(base::BindOnce(&UsbServiceWin::DeviceReady,
weak_factory_.GetWeakPtr(), device)); weak_factory_.GetWeakPtr(), device,
driver_name));
} }
void UsbServiceWin::UpdateFunction( void UsbServiceWin::UpdateFunction(
...@@ -639,6 +693,7 @@ void UsbServiceWin::UpdateFunction( ...@@ -639,6 +693,7 @@ void UsbServiceWin::UpdateFunction(
} }
void UsbServiceWin::DeviceReady(scoped_refptr<UsbDeviceWin> device, void UsbServiceWin::DeviceReady(scoped_refptr<UsbDeviceWin> device,
const std::wstring& driver_name,
bool success) { bool success) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
...@@ -664,8 +719,8 @@ void UsbServiceWin::DeviceReady(scoped_refptr<UsbDeviceWin> device, ...@@ -664,8 +719,8 @@ void UsbServiceWin::DeviceReady(scoped_refptr<UsbDeviceWin> device,
<< device->manufacturer_string() << device->manufacturer_string()
<< "\", product=" << device->product_id() << " \"" << "\", product=" << device->product_id() << " \""
<< device->product_string() << "\", serial=\"" << device->product_string() << "\", serial=\""
<< device->serial_number() << "\", driver=\"" << device->serial_number() << "\", driver=\"" << driver_name
<< device->driver_name() << "\", guid=" << device->guid(); << "\", guid=" << device->guid();
} else { } else {
devices_by_path_.erase(it); devices_by_path_.erase(it);
} }
......
...@@ -47,12 +47,15 @@ class UsbServiceWin final : public DeviceMonitorWin::Observer, ...@@ -47,12 +47,15 @@ class UsbServiceWin final : public DeviceMonitorWin::Observer,
const base::flat_map<int, UsbDeviceWin::FunctionInfo>& functions, const base::flat_map<int, UsbDeviceWin::FunctionInfo>& functions,
uint32_t bus_number, uint32_t bus_number,
uint32_t port_number, uint32_t port_number,
bool is_supported,
const std::wstring& driver_name); const std::wstring& driver_name);
void UpdateFunction(const std::wstring& device_path, void UpdateFunction(const std::wstring& device_path,
int interface_number, int interface_number,
const UsbDeviceWin::FunctionInfo& function_info); 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() { bool enumeration_ready() {
return helper_started_ && first_enumeration_countdown_ == 0; 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