| // 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 <cstdlib> |
| |
| #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 <windows.h> |
| #include <hidclass.h> |
| |
| extern "C" { |
| |
| #include <hidsdi.h> |
| #include <hidpi.h> |
| |
| } |
| |
| #include <setupapi.h> |
| #include <winioctl.h> |
| #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); |
| |
| std::set<std::string> connected_devices; |
| |
| if (device_info_set != INVALID_HANDLE_VALUE) { |
| 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<SP_DEVICE_INTERFACE_DETAIL_DATA_A, base::FreeDeleter> |
| device_interface_detail_data( |
| static_cast<SP_DEVICE_INTERFACE_DETAIL_DATA_A*>( |
| 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); |
| connected_devices.insert(device_interface_detail_data->DevicePath); |
| } |
| } |
| |
| // Find disconnected devices. |
| const DeviceMap& devices = GetDevicesNoEnumerate(); |
| std::vector<std::string> disconnected_devices; |
| for (DeviceMap::const_iterator it = devices.begin(); |
| it != devices.end(); |
| ++it) { |
| if (!ContainsKey(connected_devices, it->first)) { |
| disconnected_devices.push_back(it->first); |
| } |
| } |
| |
| // Remove disconnected devices. |
| for (size_t i = 0; i < disconnected_devices.size(); ++i) { |
| PlatformRemoveDevice(disconnected_devices[i]); |
| } |
| } |
| |
| 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<HidUsageAndPage::Page>(capabilities.UsagePage)); |
| USHORT button_caps_length = capabilities.NumberInputButtonCaps; |
| if (button_caps_length > 0) { |
| scoped_ptr<HIDP_BUTTON_CAPS[]> 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<HIDP_VALUE_CAPS[]> 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<HidDeviceInfo>* devices) { |
| Enumerate(); |
| HidService::GetDevices(devices); |
| } |
| |
| scoped_refptr<HidConnection> HidServiceWin::Connect( |
| const HidDeviceId& device_id) { |
| HidDeviceInfo device_info; |
| if (!GetDeviceInfo(device_id, &device_info)) |
| return NULL; |
| scoped_refptr<HidConnectionWin> connection(new HidConnectionWin(device_info)); |
| if (!connection->available()) { |
| PLOG(ERROR) << "Failed to open device."; |
| return NULL; |
| } |
| return connection; |
| } |
| |
| } // namespace device |