diff options
Diffstat (limited to 'chromium/device/hid/hid_service_win.cc')
-rw-r--r-- | chromium/device/hid/hid_service_win.cc | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/chromium/device/hid/hid_service_win.cc b/chromium/device/hid/hid_service_win.cc new file mode 100644 index 00000000000..82477a52845 --- /dev/null +++ b/chromium/device/hid/hid_service_win.cc @@ -0,0 +1,250 @@ +// 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); + + 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<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); + } +} + +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; + if (HidP_GetCaps(preparsed_data, &capabilities) == HIDP_STATUS_SUCCESS) { + device_info.input_report_size = capabilities.InputReportByteLength; + device_info.output_report_size = capabilities.OutputReportByteLength; + device_info.feature_report_size = capabilities.FeatureReportByteLength; + device_info.usages.push_back(HidUsageAndPage( + capabilities.Usage, + static_cast<HidUsageAndPage::Page>(capabilities.UsagePage))); + } + // Detect if the device supports report ids. + if (capabilities.NumberInputValueCaps > 0) { + scoped_ptr<HIDP_VALUE_CAPS[]> value_caps( + new HIDP_VALUE_CAPS[capabilities.NumberInputValueCaps]); + USHORT value_caps_length = capabilities.NumberInputValueCaps; + if (HidP_GetValueCaps(HidP_Input, &value_caps[0], &value_caps_length, + preparsed_data) == HIDP_STATUS_SUCCESS) { + device_info.has_report_id = (value_caps[0].ReportID != 0); + } + } + if (!device_info.has_report_id && capabilities.NumberInputButtonCaps > 0) + { + scoped_ptr<HIDP_BUTTON_CAPS[]> button_caps( + new HIDP_BUTTON_CAPS[capabilities.NumberInputButtonCaps]); + USHORT button_caps_length = capabilities.NumberInputButtonCaps; + if (HidP_GetButtonCaps(HidP_Input, + &button_caps[0], + &button_caps_length, + preparsed_data) == HIDP_STATUS_SUCCESS) { + device_info.has_report_id = (button_caps[0].ReportID != 0); + } + } + + 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 |