123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684 |
- // Copyright 2014 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/hid/hid_service_win.h"
- #define INITGUID
- #include <dbt.h>
- #include <devpkey.h>
- #include <setupapi.h>
- #include <stddef.h>
- #include <wdmguid.h>
- #include <winioctl.h>
- #include <algorithm>
- #include <limits>
- #include <memory>
- #include <set>
- #include <utility>
- #include "base/bind.h"
- #include "base/callback_helpers.h"
- #include "base/files/file.h"
- #include "base/location.h"
- #include "base/memory/free_deleter.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/task/sequenced_task_runner.h"
- #include "base/task/thread_pool.h"
- #include "base/threading/sequenced_task_runner_handle.h"
- #include "base/win/scoped_devinfo.h"
- #include "base/win/win_util.h"
- #include "components/device_event_log/device_event_log.h"
- #include "services/device/hid/hid_connection_win.h"
- #include "services/device/hid/hid_device_info.h"
- #include "services/device/hid/hid_preparsed_data.h"
- namespace device {
- namespace {
- // Flags for the BitField member of HIDP_BUTTON_CAPS and HIDP_VALUE_CAPS. This
- // bitfield is defined in the Device Class Definition for HID v1.11 section
- // 6.2.2.5.
- // https://www.usb.org/document-library/device-class-definition-hid-111
- constexpr uint16_t kBitFieldFlagConstant = 1 << 0;
- constexpr uint16_t kBitFieldFlagVariable = 1 << 1;
- constexpr uint16_t kBitFieldFlagRelative = 1 << 2;
- constexpr uint16_t kBitFieldFlagWrap = 1 << 3;
- constexpr uint16_t kBitFieldFlagNonLinear = 1 << 4;
- constexpr uint16_t kBitFieldFlagNoPreferredState = 1 << 5;
- constexpr uint16_t kBitFieldFlagHasNullPosition = 1 << 6;
- constexpr uint16_t kBitFieldFlagVolatile = 1 << 7;
- constexpr uint16_t kBitFieldFlagBufferedBytes = 1 << 8;
- // Unpacks |bit_field| into the corresponding members of |item|.
- void UnpackBitField(uint16_t bit_field, mojom::HidReportItem* item) {
- item->is_constant = bit_field & kBitFieldFlagConstant;
- item->is_variable = bit_field & kBitFieldFlagVariable;
- item->is_relative = bit_field & kBitFieldFlagRelative;
- item->wrap = bit_field & kBitFieldFlagWrap;
- item->is_non_linear = bit_field & kBitFieldFlagNonLinear;
- item->no_preferred_state = bit_field & kBitFieldFlagNoPreferredState;
- item->has_null_position = bit_field & kBitFieldFlagHasNullPosition;
- item->is_volatile = bit_field & kBitFieldFlagVolatile;
- item->is_buffered_bytes = bit_field & kBitFieldFlagBufferedBytes;
- }
- // Looks up the value of a string device property specified by |property_key|
- // for the device described by |device_info_data|. On success, returns the
- // property value as a wstring. Returns absl::nullopt if the property is not
- // present or has a different type.
- absl::optional<std::wstring> GetDeviceStringProperty(
- HDEVINFO device_info_set,
- SP_DEVINFO_DATA& device_info_data,
- const DEVPROPKEY& property_key) {
- DEVPROPTYPE property_type;
- DWORD required_size;
- if (SetupDiGetDeviceProperty(device_info_set, &device_info_data,
- &property_key, &property_type,
- /*PropertyBuffer=*/nullptr,
- /*PropertyBufferSize=*/0, &required_size,
- /*Flags=*/0)) {
- HID_LOG(DEBUG) << "SetupDiGetDeviceProperty unexpectedly succeeded.";
- return absl::nullopt;
- }
- DWORD last_error = GetLastError();
- if (last_error == ERROR_NOT_FOUND)
- return absl::nullopt;
- if (last_error != ERROR_INSUFFICIENT_BUFFER) {
- HID_PLOG(DEBUG) << "SetupDiGetDeviceProperty failed";
- return absl::nullopt;
- }
- if (property_type != DEVPROP_TYPE_STRING)
- return absl::nullopt;
- std::wstring property_buffer;
- if (!SetupDiGetDeviceProperty(
- device_info_set, &device_info_data, &property_key, &property_type,
- reinterpret_cast<PBYTE>(
- base::WriteInto(&property_buffer, required_size)),
- required_size, /*RequiredSize=*/nullptr, /*Flags=*/0)) {
- HID_PLOG(DEBUG) << "SetupDiGetDeviceProperty failed";
- return absl::nullopt;
- }
- return property_buffer;
- }
- // Looks up the value of a GUID-type device property specified by |property| for
- // the device described by |device_info_data|. On success, returns the property
- // value as a string. Returns absl::nullopt if the property is not present or
- // has a different type.
- absl::optional<std::string> GetDeviceGuidProperty(
- HDEVINFO device_info_set,
- SP_DEVINFO_DATA& device_info_data,
- const DEVPROPKEY& property_key) {
- DEVPROPTYPE property_type;
- GUID property_buffer;
- if (!SetupDiGetDeviceProperty(
- device_info_set, &device_info_data, &property_key, &property_type,
- reinterpret_cast<PBYTE>(&property_buffer), sizeof(property_buffer),
- /*RequiredSize=*/nullptr, /*Flags=*/0)) {
- HID_PLOG(DEBUG) << "SetupDiGetDeviceProperty failed";
- return absl::nullopt;
- }
- if (property_type != DEVPROP_TYPE_GUID)
- return absl::nullopt;
- return base::SysWideToUTF8(base::win::WStringFromGUID(property_buffer));
- }
- // Looks up information about the device described by |device_interface_data|
- // in |device_info_set|. On success, returns true and sets |device_info_data|
- // and |device_path|. Returns false if an error occurred.
- bool GetDeviceInfoAndPathFromInterface(
- HDEVINFO device_info_set,
- SP_DEVICE_INTERFACE_DATA& device_interface_data,
- SP_DEVINFO_DATA* device_info_data,
- std::wstring* device_path) {
- // Get the required buffer size. When called with
- // DeviceInterfaceDetailData == nullptr and DeviceInterfaceDetailSize == 0,
- // SetupDiGetDeviceInterfaceDetail returns the required buffer size at
- // RequiredSize and fails with GetLastError() == ERROR_INSUFFICIENT_BUFFER.
- DWORD required_size;
- if (SetupDiGetDeviceInterfaceDetail(device_info_set, &device_interface_data,
- /*DeviceInterfaceDetailData=*/nullptr,
- /*DeviceInterfaceDetailSize=*/0,
- &required_size,
- /*DeviceInfoData=*/nullptr)) {
- HID_LOG(DEBUG) << "SetupDiGetDeviceInterfaceDetail unexpectedly succeeded.";
- return false;
- }
- if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
- HID_PLOG(DEBUG) << "SetupDiGetDeviceInterfaceDetail failed";
- 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);
- // Call the function again with the correct buffer size to get the detailed
- // data for this device.
- if (!SetupDiGetDeviceInterfaceDetail(device_info_set, &device_interface_data,
- device_interface_detail_data.get(),
- required_size, /*RequiredSize=*/nullptr,
- device_info_data)) {
- HID_PLOG(DEBUG) << "SetupDiGetDeviceInterfaceDetail failed";
- return false;
- }
- // Windows uses case-insensitive paths and may return paths that differ only
- // by case. Canonicalize the device path by converting to lowercase.
- std::wstring path = device_interface_detail_data->DevicePath;
- DCHECK(base::IsStringASCII(path));
- *device_path = base::ToLowerASCII(path);
- return true;
- }
- // Returns a device info set containing only the device described by
- // |device_path|, or an invalid ScopedDevInfo if there was an error while
- // creating the device set. The device info is returned in |device_info_data|.
- base::win::ScopedDevInfo GetDeviceInfoSetFromDevicePath(
- const std::wstring& device_path,
- SP_DEVINFO_DATA* device_info_data) {
- base::win::ScopedDevInfo device_info_set(SetupDiGetClassDevs(
- &GUID_DEVINTERFACE_HID, /*Enumerator=*/nullptr,
- /*hwndParent=*/0, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT));
- if (!device_info_set.is_valid()) {
- HID_PLOG(DEBUG) << "SetupDiGetClassDevs failed";
- return base::win::ScopedDevInfo();
- }
- SP_DEVICE_INTERFACE_DATA device_interface_data;
- device_interface_data.cbSize = sizeof(device_interface_data);
- if (!SetupDiOpenDeviceInterface(device_info_set.get(), device_path.c_str(),
- /*OpenFlags=*/0, &device_interface_data)) {
- HID_PLOG(DEBUG) << "SetupDiOpenDeviceInterface failed";
- return base::win::ScopedDevInfo();
- }
- std::wstring intf_device_path;
- GetDeviceInfoAndPathFromInterface(device_info_set.get(),
- device_interface_data, device_info_data,
- &intf_device_path);
- DCHECK_EQ(intf_device_path, device_path);
- return device_info_set;
- }
- // Returns the instance ID of the parent of the device described by
- // |device_interface_data| in |device_info_set|. Returns nullopt if the parent
- // instance ID could not be retrieved.
- absl::optional<std::wstring> GetParentInstanceId(
- HDEVINFO device_info_set,
- SP_DEVICE_INTERFACE_DATA& device_interface_data) {
- // Get device info for |device_interface_data|.
- SP_DEVINFO_DATA device_info_data = {.cbSize = sizeof(device_info_data)};
- std::wstring device_path;
- if (!GetDeviceInfoAndPathFromInterface(device_info_set, device_interface_data,
- &device_info_data, &device_path)) {
- return absl::nullopt;
- }
- // Get the parent instance ID.
- auto instance_id = GetDeviceStringProperty(device_info_set, device_info_data,
- DEVPKEY_Device_Parent);
- if (!instance_id)
- return absl::nullopt;
- // Canonicalize the instance ID.
- DCHECK(base::IsStringASCII(*instance_id));
- instance_id = base::ToLowerASCII(*instance_id);
- // Remove trailing NUL bytes.
- return std::wstring(base::TrimString(
- *instance_id, base::WStringPiece(L"\0", 1), base::TRIM_TRAILING));
- }
- mojom::HidReportItemPtr CreateHidReportItem(
- const HidServiceWin::PreparsedData::ReportItem& item) {
- auto hid_report_item = mojom::HidReportItem::New();
- UnpackBitField(item.bit_field, hid_report_item.get());
- if (item.usage_minimum == item.usage_maximum) {
- hid_report_item->is_range = false;
- hid_report_item->usages.push_back(
- mojom::HidUsageAndPage::New(item.usage_minimum, item.usage_page));
- hid_report_item->usage_minimum = mojom::HidUsageAndPage::New(0, 0);
- hid_report_item->usage_maximum = mojom::HidUsageAndPage::New(0, 0);
- } else {
- hid_report_item->is_range = true;
- hid_report_item->usage_minimum =
- mojom::HidUsageAndPage::New(item.usage_minimum, item.usage_page);
- hid_report_item->usage_maximum =
- mojom::HidUsageAndPage::New(item.usage_maximum, item.usage_page);
- }
- hid_report_item->designator_minimum = item.designator_minimum;
- hid_report_item->designator_maximum = item.designator_maximum;
- hid_report_item->string_minimum = item.string_minimum;
- hid_report_item->string_maximum = item.string_maximum;
- hid_report_item->logical_minimum = item.logical_minimum;
- hid_report_item->logical_maximum = item.logical_maximum;
- hid_report_item->physical_minimum = item.physical_minimum;
- hid_report_item->physical_maximum = item.physical_maximum;
- hid_report_item->unit_exponent = item.unit_exponent;
- hid_report_item->unit = item.unit;
- hid_report_item->report_size = item.report_size;
- hid_report_item->report_count = item.report_count;
- return hid_report_item;
- }
- // Returns a mojom::HidReportItemPtr representing a constant (zero) field within
- // a report. |bit_size| is the bit width of the constant field.
- mojom::HidReportItemPtr CreateConstHidReportItem(uint16_t bit_size) {
- auto hid_report_item = mojom::HidReportItem::New();
- hid_report_item->is_constant = true;
- hid_report_item->report_count = 1;
- hid_report_item->report_size = bit_size;
- hid_report_item->usage_minimum = mojom::HidUsageAndPage::New(0, 0);
- hid_report_item->usage_maximum = mojom::HidUsageAndPage::New(0, 0);
- return hid_report_item;
- }
- // Returns a vector of mojom::HidReportDescriptionPtr constructed from the
- // information about the top-level collection described by |preparsed_data|.
- // The returned vector contains information about all reports of type
- // |report_type|.
- std::vector<mojom::HidReportDescriptionPtr> CreateReportDescriptions(
- const HidServiceWin::PreparsedData& preparsed_data,
- HIDP_REPORT_TYPE report_type) {
- auto report_items = preparsed_data.GetReportItems(report_type);
- // Sort items by |report_id| and |bit_index|.
- base::ranges::sort(report_items, [](const auto& a, const auto& b) {
- if (a.report_id < b.report_id)
- return true;
- if (a.report_id == b.report_id)
- return a.bit_index < b.bit_index;
- return false;
- });
- std::vector<mojom::HidReportDescriptionPtr> reports;
- mojom::HidReportDescription* current_report = nullptr;
- mojom::HidReportItem* current_item = nullptr;
- size_t current_bit_index = 0;
- size_t next_bit_index = 0;
- for (const auto& item : report_items) {
- if (!current_report || current_report->report_id != item.report_id) {
- reports.push_back(mojom::HidReportDescription::New());
- current_report = reports.back().get();
- current_report->report_id = item.report_id;
- current_item = nullptr;
- current_bit_index = 0;
- next_bit_index = 0;
- }
- // If |item| occupies the same bit index as |current_item| then they must be
- // merged into a single HidReportItem. This can occur when a report item is
- // defined with a list of usages instead of a usage range.
- if (current_item && current_bit_index == item.bit_index) {
- // Usage ranges cannot be merged into a single item. Ensure that both
- // |item| and |current_item| are single-usage items. If either has a usage
- // range, omit |item| from the report.
- if (!current_item->is_range && item.usage_minimum == item.usage_maximum) {
- current_item->usages.push_back(
- mojom::HidUsageAndPage::New(item.usage_minimum, item.usage_page));
- }
- continue;
- }
- // If there is a gap between the last bit of |current_item| and the first
- // bit of |item|, insert a constant item for padding.
- if (next_bit_index < item.bit_index) {
- size_t pad_bits = item.bit_index - next_bit_index;
- current_report->items.push_back(CreateConstHidReportItem(pad_bits));
- }
- current_report->items.push_back(CreateHidReportItem(item));
- current_item = current_report->items.back().get();
- current_bit_index = item.bit_index;
- next_bit_index = item.bit_index + item.report_size * item.report_count;
- }
- // Compute the size of each report and, if needed, add a final constant item
- // to pad the report to the expected report byte length.
- const size_t report_byte_length =
- preparsed_data.GetReportByteLength(report_type);
- for (auto& report : reports) {
- size_t bit_length = 0;
- for (auto& item : report->items)
- bit_length += item->report_size * item->report_count;
- DCHECK_LE(bit_length, report_byte_length * CHAR_BIT);
- size_t pad_bits = report_byte_length * CHAR_BIT - bit_length;
- if (pad_bits > 0)
- report->items.push_back(CreateConstHidReportItem(pad_bits));
- }
- return reports;
- }
- // Buffer size for calls to HidD_Get*String methods. 1023 characters plus NUL
- // terminator is more than enough for a USB string descriptor which is limited
- // to 126 characters.
- constexpr size_t kBufferSize = 1024;
- std::string GetHidProductString(HANDLE device_handle) {
- // HidD_Get*String methods may return successfully even when they do not write
- // to the output buffer. Ensure the buffer is zeroed before calling. See
- // https://crbug.com/1205511.
- std::wstring buffer;
- if (!HidD_GetProductString(
- device_handle, base::WriteInto(&buffer, kBufferSize), kBufferSize)) {
- return std::string();
- }
- // HidD_GetProductString is guaranteed to write a NUL-terminated string into
- // |buffer|. The characters following the string were value-initialized by
- // base::WriteInto and are also NUL. Trim the trailing NUL characters.
- buffer = std::wstring(base::TrimString(buffer, base::WStringPiece(L"\0", 1),
- base::TRIM_TRAILING));
- return base::SysWideToUTF8(buffer);
- }
- std::string GetHidSerialNumberString(HANDLE device_handle) {
- // HidD_Get*String methods may return successfully even when they do not write
- // to the output buffer. Ensure the buffer is zeroed before calling. See
- // https://crbug.com/1205511.
- std::wstring buffer;
- if (!HidD_GetSerialNumberString(
- device_handle, base::WriteInto(&buffer, kBufferSize), kBufferSize)) {
- return std::string();
- }
- // HidD_GetSerialNumberString is guaranteed to write a NUL-terminated string
- // into |buffer|. The characters following the string were value-initialized
- // by base::WriteInto and are also NUL. Trim the trailing NUL characters.
- buffer = std::wstring(base::TrimString(buffer, base::WStringPiece(L"\0", 1),
- base::TRIM_TRAILING));
- return base::SysWideToUTF8(buffer);
- }
- } // namespace
- mojom::HidCollectionInfoPtr
- HidServiceWin::PreparsedData::CreateHidCollectionInfo() const {
- const HIDP_CAPS& caps = GetCaps();
- auto collection_info = mojom::HidCollectionInfo::New();
- collection_info->usage =
- mojom::HidUsageAndPage::New(caps.Usage, caps.UsagePage);
- collection_info->input_reports = CreateReportDescriptions(*this, HidP_Input);
- collection_info->output_reports =
- CreateReportDescriptions(*this, HidP_Output);
- collection_info->feature_reports =
- CreateReportDescriptions(*this, HidP_Feature);
- // Collect and de-duplicate report IDs.
- std::set<uint8_t> report_ids;
- for (const auto& report : collection_info->input_reports) {
- if (report->report_id)
- report_ids.insert(report->report_id);
- }
- for (const auto& report : collection_info->output_reports) {
- if (report->report_id)
- report_ids.insert(report->report_id);
- }
- for (const auto& report : collection_info->feature_reports) {
- if (report->report_id)
- report_ids.insert(report->report_id);
- }
- collection_info->report_ids.insert(collection_info->report_ids.end(),
- report_ids.begin(), report_ids.end());
- return collection_info;
- }
- uint16_t HidServiceWin::PreparsedData::GetReportByteLength(
- HIDP_REPORT_TYPE report_type) const {
- uint16_t report_length = 0;
- switch (report_type) {
- case HidP_Input:
- report_length = GetCaps().InputReportByteLength;
- break;
- case HidP_Output:
- report_length = GetCaps().OutputReportByteLength;
- break;
- case HidP_Feature:
- report_length = GetCaps().FeatureReportByteLength;
- break;
- default:
- NOTREACHED();
- break;
- }
- // Whether or not the device includes report IDs in its reports the size
- // of the report ID is included in the value provided by Windows. This
- // appears contrary to the MSDN documentation.
- if (report_length)
- return report_length - 1;
- return 0;
- }
- HidServiceWin::HidServiceWin()
- : task_runner_(base::SequencedTaskRunnerHandle::Get()),
- blocking_task_runner_(
- base::ThreadPool::CreateSequencedTaskRunner(kBlockingTaskTraits)) {
- DeviceMonitorWin* device_monitor =
- DeviceMonitorWin::GetForDeviceInterface(GUID_DEVINTERFACE_HID);
- if (device_monitor)
- device_observation_.Observe(device_monitor);
- blocking_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&HidServiceWin::EnumerateBlocking,
- weak_factory_.GetWeakPtr(), task_runner_));
- }
- HidServiceWin::~HidServiceWin() = default;
- void HidServiceWin::Connect(const std::string& device_guid,
- bool allow_protected_reports,
- bool allow_fido_reports,
- ConnectCallback callback) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- const auto& map_entry = devices().find(device_guid);
- if (map_entry == devices().end()) {
- task_runner_->PostTask(FROM_HERE,
- base::BindOnce(std::move(callback), nullptr));
- return;
- }
- scoped_refptr<HidDeviceInfo> device_info = map_entry->second;
- const auto& platform_device_id_map = device_info->platform_device_id_map();
- std::vector<std::unique_ptr<HidConnectionWin::HidDeviceEntry>> file_handles;
- for (const auto& entry : platform_device_id_map) {
- base::win::ScopedHandle file_handle(OpenDevice(entry.platform_device_id));
- if (!file_handle.IsValid()) {
- HID_PLOG(DEBUG) << "Failed to open device with deviceId='"
- << entry.platform_device_id << "'";
- continue;
- }
- file_handles.push_back(std::make_unique<HidConnectionWin::HidDeviceEntry>(
- entry.report_ids, std::move(file_handle)));
- }
- if (file_handles.empty()) {
- // Report failure if none of the file handles could be opened.
- task_runner_->PostTask(FROM_HERE,
- base::BindOnce(std::move(callback), nullptr));
- return;
- }
- task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(std::move(callback),
- HidConnectionWin::Create(
- device_info, std::move(file_handles),
- allow_protected_reports, allow_fido_reports)));
- }
- base::WeakPtr<HidService> HidServiceWin::GetWeakPtr() {
- return weak_factory_.GetWeakPtr();
- }
- // static
- void HidServiceWin::EnumerateBlocking(
- base::WeakPtr<HidServiceWin> service,
- scoped_refptr<base::SequencedTaskRunner> task_runner) {
- base::win::ScopedDevInfo device_info_set(SetupDiGetClassDevs(
- &GUID_DEVINTERFACE_HID, /*Enumerator=*/nullptr,
- /*hwndParent=*/nullptr, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE));
- if (device_info_set.is_valid()) {
- SP_DEVICE_INTERFACE_DATA device_interface_data = {
- .cbSize = sizeof(device_interface_data)};
- for (int device_index = 0; SetupDiEnumDeviceInterfaces(
- device_info_set.get(), /*DeviceInfoData=*/nullptr,
- &GUID_DEVINTERFACE_HID, device_index, &device_interface_data);
- ++device_index) {
- SP_DEVINFO_DATA device_info_data = {.cbSize = sizeof(device_info_data)};
- std::wstring device_path;
- if (!GetDeviceInfoAndPathFromInterface(device_info_set.get(),
- device_interface_data,
- &device_info_data, &device_path)) {
- continue;
- }
- // Get the container ID for the physical device.
- auto physical_device_id = GetDeviceGuidProperty(
- device_info_set.get(), device_info_data, DEVPKEY_Device_ContainerId);
- if (!physical_device_id)
- continue;
- auto interface_id =
- GetParentInstanceId(device_info_set.get(), device_interface_data);
- if (!interface_id)
- continue;
- AddDeviceBlocking(service, task_runner, device_path, *physical_device_id,
- *interface_id);
- }
- }
- task_runner->PostTask(
- FROM_HERE,
- base::BindOnce(&HidServiceWin::FirstEnumerationComplete, service));
- }
- // static
- void HidServiceWin::AddDeviceBlocking(
- base::WeakPtr<HidServiceWin> service,
- scoped_refptr<base::SequencedTaskRunner> task_runner,
- const std::wstring& device_path,
- const std::string& physical_device_id,
- const std::wstring& interface_id) {
- base::win::ScopedHandle device_handle(OpenDevice(device_path));
- if (!device_handle.IsValid())
- return;
- auto preparsed_data = HidPreparsedData::Create(device_handle.Get());
- if (!preparsed_data)
- return;
- HIDD_ATTRIBUTES attrib = {.Size = sizeof(attrib)};
- if (!HidD_GetAttributes(device_handle.Get(), &attrib)) {
- HID_LOG(DEBUG) << "Failed to get device attributes.";
- return;
- }
- uint16_t vendor_id = attrib.VendorID;
- uint16_t product_id = attrib.ProductID;
- auto product_string = GetHidProductString(device_handle.Get());
- auto serial_number = GetHidSerialNumberString(device_handle.Get());
- // Create a HidCollectionInfo for |device_path| and update the relevant
- // HidDeviceInfo properties.
- auto collection = preparsed_data->CreateHidCollectionInfo();
- uint16_t max_input_report_size =
- preparsed_data->GetReportByteLength(HidP_Input);
- uint16_t max_output_report_size =
- preparsed_data->GetReportByteLength(HidP_Output);
- uint16_t max_feature_report_size =
- preparsed_data->GetReportByteLength(HidP_Feature);
- // This populates the HidDeviceInfo instance without a raw report descriptor.
- // The descriptor is unavailable on Windows.
- auto device_info = base::MakeRefCounted<HidDeviceInfo>(
- device_path, physical_device_id, base::SysWideToUTF8(interface_id),
- vendor_id, product_id, product_string, serial_number,
- // TODO(crbug.com/443335): Detect Bluetooth.
- mojom::HidBusType::kHIDBusTypeUSB, std::move(collection),
- max_input_report_size, max_output_report_size, max_feature_report_size);
- task_runner->PostTask(FROM_HERE,
- base::BindOnce(&HidServiceWin::AddDevice, service,
- std::move(device_info)));
- }
- void HidServiceWin::OnDeviceAdded(const GUID& class_guid,
- const std::wstring& device_path) {
- SP_DEVINFO_DATA device_info_data = {.cbSize = sizeof(device_info_data)};
- auto device_info_set =
- GetDeviceInfoSetFromDevicePath(device_path, &device_info_data);
- if (!device_info_set.is_valid())
- return;
- // Assume there is at most one matching device.
- SP_DEVICE_INTERFACE_DATA device_interface_data;
- device_interface_data.cbSize = sizeof(device_interface_data);
- if (!SetupDiEnumDeviceInterfaces(device_info_set.get(), &device_info_data,
- &GUID_DEVINTERFACE_HID,
- /*MemberIndex=*/0, &device_interface_data)) {
- HID_PLOG(DEBUG) << "SetupDiEnumDeviceInterfaces failed";
- return;
- }
- // Get the container ID for the physical device.
- auto physical_device_id = GetDeviceGuidProperty(
- device_info_set.get(), device_info_data, DEVPKEY_Device_ContainerId);
- if (!physical_device_id)
- return;
- // The parent device represents the HID interface.
- auto interface_id =
- GetParentInstanceId(device_info_set.get(), device_interface_data);
- if (!interface_id)
- return;
- blocking_task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(&HidServiceWin::AddDeviceBlocking,
- weak_factory_.GetWeakPtr(), task_runner_, device_path,
- *physical_device_id, *interface_id));
- }
- void HidServiceWin::OnDeviceRemoved(const GUID& class_guid,
- const std::wstring& device_path) {
- // Execute a no-op closure on the file task runner to synchronize with any
- // devices that are still being enumerated.
- blocking_task_runner_->PostTaskAndReply(
- FROM_HERE, base::DoNothing(),
- base::BindOnce(&HidServiceWin::RemoveDevice, weak_factory_.GetWeakPtr(),
- device_path));
- }
- // static
- base::win::ScopedHandle HidServiceWin::OpenDevice(
- const std::wstring& device_path) {
- base::win::ScopedHandle file(
- CreateFile(device_path.c_str(), GENERIC_WRITE | GENERIC_READ,
- FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING,
- FILE_FLAG_OVERLAPPED, nullptr));
- if (!file.IsValid() && GetLastError() == ERROR_ACCESS_DENIED) {
- file.Set(CreateFile(device_path.c_str(), GENERIC_READ, FILE_SHARE_READ,
- nullptr, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, nullptr));
- }
- return file;
- }
- } // namespace device
|