| // Copyright 2017 The Chromium Authors |
| // 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_device_win.h" |
| |
| #include <windows.h> |
| |
| #include <utility> |
| |
| #include "base/functional/bind.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/strings/string_util.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "components/device_event_log/device_event_log.h" |
| #include "services/device/usb/usb_device_handle_win.h" |
| #include "services/device/usb/webusb_descriptors.h" |
| |
| namespace device { |
| |
| namespace { |
| const uint16_t kUsbVersion2_1 = 0x0210; |
| } // namespace |
| |
| UsbDeviceWin::UsbDeviceWin(const std::wstring& device_path, |
| const std::wstring& hub_path, |
| const base::flat_map<int, FunctionInfo>& functions, |
| uint32_t bus_number, |
| uint32_t port_number, |
| DriverType driver_type) |
| : UsbDevice(bus_number, port_number), |
| device_path_(device_path), |
| hub_path_(hub_path), |
| functions_(functions), |
| driver_type_(driver_type) {} |
| |
| UsbDeviceWin::~UsbDeviceWin() {} |
| |
| void UsbDeviceWin::Open(OpenCallback callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| scoped_refptr<UsbDeviceHandle> device_handle; |
| if (driver_type_ != DriverType::kUnsupported) { |
| device_handle = new UsbDeviceHandleWin(this); |
| handles().push_back(device_handle.get()); |
| } |
| |
| base::SequencedTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(callback), std::move(device_handle))); |
| } |
| |
| void UsbDeviceWin::ReadDescriptors( |
| scoped_refptr<base::SequencedTaskRunner> blocking_task_runner, |
| base::OnceCallback<void(bool)> callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| scoped_refptr<UsbDeviceHandle> device_handle; |
| base::win::ScopedHandle handle( |
| CreateFile(hub_path_.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, nullptr, |
| OPEN_EXISTING, FILE_FLAG_OVERLAPPED, nullptr)); |
| if (handle.IsValid()) { |
| device_handle = new UsbDeviceHandleWin(this, std::move(handle), |
| std::move(blocking_task_runner)); |
| } else { |
| USB_PLOG(ERROR) << "Failed to open " << hub_path_; |
| std::move(callback).Run(false); |
| return; |
| } |
| |
| ReadUsbDescriptors(device_handle, |
| base::BindOnce(&UsbDeviceWin::OnReadDescriptors, this, |
| std::move(callback), device_handle)); |
| } |
| |
| void UsbDeviceWin::UpdateFunction(int interface_number, |
| const FunctionInfo& function_info) { |
| functions_[interface_number] = function_info; |
| |
| for (UsbDeviceHandle* handle : handles()) { |
| // This is safe because only this class only adds instance of |
| // UsbDeviceHandleWin to handles(). |
| static_cast<UsbDeviceHandleWin*>(handle)->UpdateFunction( |
| interface_number, function_info.driver, function_info.path); |
| } |
| } |
| |
| void UsbDeviceWin::OnReadDescriptors( |
| base::OnceCallback<void(bool)> callback, |
| scoped_refptr<UsbDeviceHandle> device_handle, |
| std::unique_ptr<UsbDeviceDescriptor> descriptor) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| if (!descriptor) { |
| USB_LOG(ERROR) << "Failed to read descriptors from " << device_path_ << "."; |
| device_handle->Close(); |
| std::move(callback).Run(false); |
| return; |
| } |
| |
| // Keep |guid|, |bus_number| and |port_number| before updating the |
| // |device_info_|. |
| descriptor->device_info->guid = device_info_->guid, |
| descriptor->device_info->bus_number = device_info_->bus_number, |
| descriptor->device_info->port_number = device_info_->port_number, |
| device_info_ = std::move(descriptor->device_info); |
| |
| // The active configuration was set after reading the node connection info |
| // from the hub driver. If it wasn't valid, assume the first configuration. |
| if (!GetActiveConfiguration() && !configurations().empty()) |
| ActiveConfigurationChanged(configurations()[0]->configuration_value); |
| |
| auto string_map = std::make_unique<std::map<uint8_t, std::u16string>>(); |
| if (descriptor->i_manufacturer) |
| (*string_map)[descriptor->i_manufacturer]; |
| if (descriptor->i_product) |
| (*string_map)[descriptor->i_product]; |
| if (descriptor->i_serial_number) |
| (*string_map)[descriptor->i_serial_number]; |
| |
| ReadUsbStringDescriptors( |
| device_handle, std::move(string_map), |
| base::BindOnce(&UsbDeviceWin::OnReadStringDescriptors, this, |
| std::move(callback), device_handle, |
| descriptor->i_manufacturer, descriptor->i_product, |
| descriptor->i_serial_number)); |
| } |
| |
| void UsbDeviceWin::OnReadStringDescriptors( |
| base::OnceCallback<void(bool)> callback, |
| scoped_refptr<UsbDeviceHandle> device_handle, |
| uint8_t i_manufacturer, |
| uint8_t i_product, |
| uint8_t i_serial_number, |
| std::unique_ptr<std::map<uint8_t, std::u16string>> string_map) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| if (i_manufacturer) |
| device_info_->manufacturer_name = (*string_map)[i_manufacturer]; |
| if (i_product) |
| device_info_->product_name = (*string_map)[i_product]; |
| if (i_serial_number) |
| device_info_->serial_number = (*string_map)[i_serial_number]; |
| |
| if (usb_version() >= kUsbVersion2_1) { |
| ReadWebUsbCapabilityDescriptor( |
| device_handle, |
| base::BindOnce(&UsbDeviceWin::OnReadWebUsbCapabilityDescriptor, this, |
| std::move(callback), device_handle)); |
| } else { |
| device_handle->Close(); |
| std::move(callback).Run(true); |
| } |
| } |
| |
| void UsbDeviceWin::OnReadWebUsbCapabilityDescriptor( |
| base::OnceCallback<void(bool)> callback, |
| scoped_refptr<UsbDeviceHandle> device_handle, |
| const std::optional<WebUsbPlatformCapabilityDescriptor>& descriptor) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| device_handle->Close(); |
| |
| if (!descriptor || !descriptor->landing_page_id) { |
| std::move(callback).Run(true); |
| return; |
| } |
| |
| Open(base::BindOnce(&UsbDeviceWin::OnOpenedToReadWebUsbLandingPage, this, |
| std::move(callback), descriptor->vendor_code, |
| descriptor->landing_page_id)); |
| } |
| |
| void UsbDeviceWin::OnOpenedToReadWebUsbLandingPage( |
| base::OnceCallback<void(bool)> callback, |
| uint8_t vendor_code, |
| uint8_t landing_page_id, |
| scoped_refptr<UsbDeviceHandle> device_handle) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| if (!device_handle) { |
| USB_LOG(ERROR) << "Failed to open device to read WebUSB descriptors."; |
| // Failure to read WebUSB descriptors is not fatal. |
| std::move(callback).Run(true); |
| return; |
| } |
| |
| ReadWebUsbLandingPage( |
| vendor_code, landing_page_id, device_handle, |
| base::BindOnce(&UsbDeviceWin::OnReadWebUsbLandingPage, this, |
| std::move(callback), device_handle)); |
| } |
| |
| void UsbDeviceWin::OnReadWebUsbLandingPage( |
| base::OnceCallback<void(bool)> callback, |
| scoped_refptr<UsbDeviceHandle> device_handle, |
| const GURL& landing_page) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| device_info_->webusb_landing_page = landing_page; |
| |
| device_handle->Close(); |
| std::move(callback).Run(true); |
| } |
| |
| } // namespace device |