// 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 "device/hid/hid_service_win.h" #include #include "base/files/file.h" #include "base/stl_util.h" #include "base/strings/sys_string_conversions.h" #include "device/hid/hid_connection_win.h" #include "device/hid/hid_device_info.h" #include "net/base/io_buffer.h" #if defined(OS_WIN) #define INITGUID #include #include extern "C" { #include #include } #include #include #include "base/win/scoped_handle.h" #endif // defined(OS_WIN) // Setup API is required to enumerate HID devices. #pragma comment(lib, "setupapi.lib") #pragma comment(lib, "hid.lib") namespace device { namespace { const char kHIDClass[] = "HIDClass"; } // namespace HidServiceWin::HidServiceWin() { Enumerate(); } HidServiceWin::~HidServiceWin() {} void HidServiceWin::Enumerate() { BOOL res; HDEVINFO device_info_set; SP_DEVINFO_DATA devinfo_data; SP_DEVICE_INTERFACE_DATA device_interface_data; memset(&devinfo_data, 0, sizeof(SP_DEVINFO_DATA)); devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA); device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); device_info_set = SetupDiGetClassDevs( &GUID_DEVINTERFACE_HID, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); if (device_info_set == INVALID_HANDLE_VALUE) return; for (int device_index = 0; SetupDiEnumDeviceInterfaces(device_info_set, NULL, &GUID_DEVINTERFACE_HID, device_index, &device_interface_data); ++device_index) { DWORD required_size = 0; // Determime the required size of detail struct. SetupDiGetDeviceInterfaceDetailA(device_info_set, &device_interface_data, NULL, 0, &required_size, NULL); scoped_ptr device_interface_detail_data( static_cast( malloc(required_size))); device_interface_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); // Get the detailed data for this device. res = SetupDiGetDeviceInterfaceDetailA(device_info_set, &device_interface_data, device_interface_detail_data.get(), required_size, NULL, NULL); if (!res) continue; // Enumerate device info. Looking for Setup Class "HIDClass". for (DWORD i = 0; SetupDiEnumDeviceInfo(device_info_set, i, &devinfo_data); i++) { char class_name[256] = {0}; res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data, SPDRP_CLASS, NULL, (PBYTE) class_name, sizeof(class_name) - 1, NULL); if (!res) break; if (memcmp(class_name, kHIDClass, sizeof(kHIDClass)) == 0) { char driver_name[256] = {0}; // Get bounded driver. res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data, SPDRP_DRIVER, NULL, (PBYTE) driver_name, sizeof(driver_name) - 1, NULL); if (res) { // Found the driver. break; } } } if (!res) continue; PlatformAddDevice(device_interface_detail_data->DevicePath); } } void HidServiceWin::PlatformAddDevice(const std::string& device_path) { HidDeviceInfo device_info; device_info.device_id = device_path; // Try to open the device. base::win::ScopedHandle device_handle( CreateFileA(device_path.c_str(), GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0)); if (!device_handle.IsValid() && GetLastError() == base::File::FILE_ERROR_ACCESS_DENIED) { base::win::ScopedHandle device_handle( CreateFileA(device_path.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0)); if (!device_handle.IsValid()) return; } // Get VID/PID pair. HIDD_ATTRIBUTES attrib = {0}; attrib.Size = sizeof(HIDD_ATTRIBUTES); if (!HidD_GetAttributes(device_handle.Get(), &attrib)) return; device_info.vendor_id = attrib.VendorID; device_info.product_id = attrib.ProductID; for (ULONG i = 32; HidD_SetNumInputBuffers(device_handle.Get(), i); i <<= 1); // Get usage and usage page (optional). PHIDP_PREPARSED_DATA preparsed_data; if (HidD_GetPreparsedData(device_handle.Get(), &preparsed_data) && preparsed_data) { HIDP_CAPS capabilities = {0}; if (HidP_GetCaps(preparsed_data, &capabilities) == HIDP_STATUS_SUCCESS) { device_info.max_input_report_size = capabilities.InputReportByteLength; device_info.max_output_report_size = capabilities.OutputReportByteLength; device_info.max_feature_report_size = capabilities.FeatureReportByteLength; HidCollectionInfo collection_info; collection_info.usage = HidUsageAndPage( capabilities.Usage, static_cast(capabilities.UsagePage)); USHORT button_caps_length = capabilities.NumberInputButtonCaps; if (button_caps_length > 0) { scoped_ptr button_caps( new HIDP_BUTTON_CAPS[button_caps_length]); if (HidP_GetButtonCaps(HidP_Input, &button_caps[0], &button_caps_length, preparsed_data) == HIDP_STATUS_SUCCESS) { for (int i = 0; i < button_caps_length; i++) { int report_id = button_caps[i].ReportID; if (report_id != 0) { collection_info.report_ids.insert(report_id); } } } } USHORT value_caps_length = capabilities.NumberInputValueCaps; if (value_caps_length > 0) { scoped_ptr value_caps( new HIDP_VALUE_CAPS[value_caps_length]); if (HidP_GetValueCaps(HidP_Input, &value_caps[0], &value_caps_length, preparsed_data) == HIDP_STATUS_SUCCESS) { for (int i = 0; i < value_caps_length; i++) { int report_id = value_caps[i].ReportID; if (report_id != 0) { collection_info.report_ids.insert(report_id); } } } } device_info.collections.push_back(collection_info); } HidD_FreePreparsedData(preparsed_data); } AddDevice(device_info); } void HidServiceWin::PlatformRemoveDevice(const std::string& device_path) { RemoveDevice(device_path); } void HidServiceWin::GetDevices(std::vector* devices) { Enumerate(); HidService::GetDevices(devices); } scoped_refptr HidServiceWin::Connect( const HidDeviceId& device_id) { HidDeviceInfo device_info; if (!GetDeviceInfo(device_id, &device_info)) return NULL; scoped_refptr connection(new HidConnectionWin(device_info)); if (!connection->available()) { PLOG(ERROR) << "Failed to open device."; return NULL; } return connection; } } // namespace device