Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2014)

Unified Diff: device/usb/usb_service_win.cc

Issue 2690383002: Add device path enumeration to the new Windows USB backend. (Closed)
Patch Set: Fix a comment. Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « device/usb/usb_service_win.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: device/usb/usb_service_win.cc
diff --git a/device/usb/usb_service_win.cc b/device/usb/usb_service_win.cc
index 6feea8913d1652e068e91d23408273b213ffbfa5..131fdb3317c854a41f2b10d3020992611c8a2e25 100644
--- a/device/usb/usb_service_win.cc
+++ b/device/usb/usb_service_win.cc
@@ -4,12 +4,386 @@
#include "device/usb/usb_service_win.h"
+#include <setupapi.h>
+#include <stdint.h>
+#include <usbiodef.h>
+
+#define INITGUID
+#include <devpkey.h>
+
+#include "base/bind.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/stl_util.h"
+#include "base/strings/string_util.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/win/scoped_handle.h"
+#include "components/device_event_log/device_event_log.h"
+#include "device/usb/usb_descriptors.h"
+#include "device/usb/usb_device_handle.h"
+#include "device/usb/webusb_descriptors.h"
+
namespace device {
+namespace {
+
+struct DevInfoScopedTraits {
+ static HDEVINFO InvalidValue() { return INVALID_HANDLE_VALUE; }
+ static void Free(HDEVINFO h) { SetupDiDestroyDeviceInfoList(h); }
+};
+
+using ScopedDevInfo = base::ScopedGeneric<HDEVINFO, DevInfoScopedTraits>;
+
+bool GetDeviceUint32Property(HDEVINFO dev_info,
+ SP_DEVINFO_DATA* dev_info_data,
+ const DEVPROPKEY& property,
+ uint32_t* property_buffer) {
+ DEVPROPTYPE property_type;
+ if (!SetupDiGetDeviceProperty(dev_info, dev_info_data, &property,
+ &property_type,
+ reinterpret_cast<PBYTE>(property_buffer),
+ sizeof(*property_buffer), nullptr, 0) ||
+ property_type != DEVPROP_TYPE_UINT32) {
+ return false;
+ }
+
+ return true;
+}
+
+bool GetDeviceStringProperty(HDEVINFO dev_info,
+ SP_DEVINFO_DATA* dev_info_data,
+ const DEVPROPKEY& property,
+ std::string* property_buffer) {
+ 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 false;
+ }
+
+ std::wstring wide_buffer;
+ if (!SetupDiGetDeviceProperty(
+ dev_info, dev_info_data, &property, &property_type,
+ reinterpret_cast<PBYTE>(base::WriteInto(&wide_buffer, required_size)),
+ required_size, nullptr, 0)) {
+ return false;
+ }
+
+ *property_buffer = base::SysWideToUTF8(wide_buffer);
+ return true;
+}
+
+bool GetDeviceInterfaceDetails(HDEVINFO dev_info,
+ SP_DEVICE_INTERFACE_DATA* device_interface_data,
+ std::string* device_path,
+ uint32_t* port_number,
+ std::string* parent_instance_id) {
+ DWORD required_size;
+ if (SetupDiGetDeviceInterfaceDetail(dev_info, device_interface_data, nullptr,
+ 0, &required_size, nullptr) ||
+ GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ return false;
+ }
+
+ std::unique_ptr<SP_DEVICE_INTERFACE_DETAIL_DATA, base::FreeDeleter>
+ device_interface_detail_data(
+ static_cast<SP_DEVICE_INTERFACE_DETAIL_DATA*>(malloc(required_size)));
+ device_interface_detail_data->cbSize = sizeof(*device_interface_detail_data);
+
+ SP_DEVINFO_DATA dev_info_data;
+ dev_info_data.cbSize = sizeof(dev_info_data);
+
+ if (!SetupDiGetDeviceInterfaceDetail(
+ dev_info, device_interface_data, device_interface_detail_data.get(),
+ required_size, nullptr, &dev_info_data)) {
+ USB_PLOG(ERROR) << "SetupDiGetDeviceInterfaceDetail";
+ return false;
+ }
+
+ if (device_path) {
+ *device_path =
+ base::SysWideToUTF8(device_interface_detail_data->DevicePath);
+ }
+
+ if (port_number) {
+ if (!GetDeviceUint32Property(dev_info, &dev_info_data,
+ DEVPKEY_Device_Address, port_number)) {
+ USB_PLOG(ERROR) << "Failed to get device address";
+ return false;
+ }
+ }
+
+ if (parent_instance_id) {
+ if (!GetDeviceStringProperty(dev_info, &dev_info_data,
+ DEVPKEY_Device_Parent, parent_instance_id)) {
+ USB_PLOG(ERROR) << "Failed to get the device parent";
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool GetHubDevicePath(const std::string& instance_id,
+ std::string* device_path) {
+ ScopedDevInfo dev_info(
+ SetupDiGetClassDevsA(&GUID_DEVINTERFACE_USB_HUB, instance_id.c_str(), 0,
+ DIGCF_DEVICEINTERFACE | DIGCF_PRESENT));
+ if (!dev_info.is_valid()) {
+ USB_PLOG(ERROR) << "SetupDiGetClassDevs";
+ return false;
+ }
+
+ SP_DEVICE_INTERFACE_DATA device_interface_data;
+ device_interface_data.cbSize = sizeof(device_interface_data);
+ if (!SetupDiEnumDeviceInterfaces(dev_info.get(), nullptr,
+ &GUID_DEVINTERFACE_USB_HUB, 0,
+ &device_interface_data)) {
+ USB_PLOG(ERROR) << "SetupDiEnumDeviceInterfaces";
+ return false;
+ }
+
+ return GetDeviceInterfaceDetails(dev_info.get(), &device_interface_data,
+ device_path, nullptr, nullptr);
+}
+
+} // namespace
+
+class UsbServiceWin::BlockingThreadHelper {
+ public:
+ explicit BlockingThreadHelper(base::WeakPtr<UsbServiceWin> service)
+ : service_task_runner_(base::ThreadTaskRunnerHandle::Get()),
+ service_(service) {}
+ ~BlockingThreadHelper() {}
+
+ void EnumerateDevices() {
+ 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::Bind(&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) {
+ std::string device_path;
+ uint32_t port_number;
+ std::string parent_instance_id;
+ if (!GetDeviceInterfaceDetails(dev_info.get(), &device_interface_data,
+ &device_path, &port_number,
+ &parent_instance_id)) {
+ continue;
+ }
+
+ std::string& hub_path = hub_paths_[parent_instance_id];
+ if (hub_path.empty()) {
+ std::string parent_path;
+ if (!GetHubDevicePath(parent_instance_id, &parent_path))
+ continue;
+
+ hub_path = parent_path;
+ }
+
+ service_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&UsbServiceWin::CreateDeviceObject, service_,
+ device_path, hub_path, port_number));
+ }
+
+ if (GetLastError() != ERROR_NO_MORE_ITEMS)
+ USB_PLOG(ERROR) << "Failed to enumerate devices";
+
+ service_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&UsbServiceWin::HelperStarted, service_));
+ }
+
+ void EnumerateDevicePath(const std::string& device_path) {
+ 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";
+ return;
+ }
+
+ SP_DEVICE_INTERFACE_DATA device_interface_data;
+ device_interface_data.cbSize = sizeof(device_interface_data);
+ if (!SetupDiOpenDeviceInterfaceA(dev_info.get(), device_path.c_str(), 0,
+ &device_interface_data)) {
+ USB_PLOG(ERROR) << "Failed to add device interface: " << device_path;
+ return;
+ }
+
+ uint32_t port_number;
+ std::string parent_instance_id;
+ if (!GetDeviceInterfaceDetails(dev_info.get(), &device_interface_data,
+ nullptr, &port_number,
+ &parent_instance_id)) {
+ return;
+ }
+
+ std::string& hub_path = hub_paths_[parent_instance_id];
+ if (hub_path.empty()) {
+ std::string parent_path;
+ if (!GetHubDevicePath(parent_instance_id, &parent_path))
+ return;
+
+ hub_path = parent_path;
+ }
+
+ service_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&UsbServiceWin::CreateDeviceObject, service_,
+ device_path, hub_path, port_number));
+ }
+
+ private:
+ std::unordered_map<std::string, std::string> 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(
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner)
- : UsbService(blocking_task_runner) {}
+ : UsbService(blocking_task_runner),
+ device_observer_(this),
+ weak_factory_(this) {
+ DeviceMonitorWin* device_monitor =
+ DeviceMonitorWin::GetForDeviceInterface(GUID_DEVINTERFACE_USB_DEVICE);
+ if (device_monitor)
+ device_observer_.Add(device_monitor);
+
+ helper_ = new BlockingThreadHelper(weak_factory_.GetWeakPtr());
+ blocking_task_runner->PostTask(
+ FROM_HERE, base::Bind(&BlockingThreadHelper::EnumerateDevices,
+ base::Unretained(helper_)));
+}
UsbServiceWin::~UsbServiceWin() {}
+void UsbServiceWin::GetDevices(const GetDevicesCallback& callback) {
+ DCHECK(CalledOnValidThread());
+ if (enumeration_ready())
+ UsbService::GetDevices(callback);
+ else
+ enumeration_callbacks_.push_back(callback);
+}
+
+void UsbServiceWin::OnDeviceAdded(const GUID& class_guid,
+ const std::string& device_path) {
+ blocking_task_runner()->PostTask(
+ FROM_HERE, base::Bind(&BlockingThreadHelper::EnumerateDevicePath,
+ base::Unretained(helper_), device_path));
+}
+
+void UsbServiceWin::OnDeviceRemoved(const GUID& class_guid,
+ const std::string& device_path) {
+ DCHECK(CalledOnValidThread());
+ 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(CalledOnValidThread());
+ 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 (const auto& callback : enumeration_callbacks_)
+ callback.Run(result);
+ enumeration_callbacks_.clear();
+ }
+}
+
+void UsbServiceWin::CreateDeviceObject(const std::string& device_path,
+ const std::string& hub_path,
+ int port_number) {
+ // 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_;
+
+ scoped_refptr<UsbDeviceWin> device(new UsbDeviceWin(
+ device_path, hub_path, port_number, blocking_task_runner()));
+ devices_by_path_[device->device_path()] = device;
+
+ // TODO(reillyg): Read device descriptors.
+ DeviceReady(device, true);
+}
+
+void UsbServiceWin::DeviceReady(scoped_refptr<UsbDeviceWin> device,
+ bool success) {
+ DCHECK(CalledOnValidThread());
+
+ 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::ContainsKey(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() << "\", 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 (const auto& callback : enumeration_callbacks_)
+ callback.Run(result);
+ enumeration_callbacks_.clear();
+ } else if (success && enumeration_ready()) {
+ NotifyDeviceAdded(device);
+ }
+}
+
} // namespace device
« no previous file with comments | « device/usb/usb_service_win.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698