Port ChromeVox navigation manager and user commands tests.
[chromium-blink-merge.git] / device / hid / hid_service_win.cc
blob3060b2b3766ecb2ac2a0e271266d001c9447baec
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "device/hid/hid_service_win.h"
7 #include <cstdlib>
9 #include "base/files/file.h"
10 #include "base/stl_util.h"
11 #include "base/strings/sys_string_conversions.h"
12 #include "device/hid/hid_connection_win.h"
13 #include "device/hid/hid_device_info.h"
14 #include "net/base/io_buffer.h"
16 #if defined(OS_WIN)
18 #define INITGUID
20 #include <setupapi.h>
21 #include <winioctl.h>
22 #include "base/win/scoped_handle.h"
24 #endif // defined(OS_WIN)
26 // Setup API is required to enumerate HID devices.
27 #pragma comment(lib, "setupapi.lib")
28 #pragma comment(lib, "hid.lib")
30 namespace device {
31 namespace {
33 const char kHIDClass[] = "HIDClass";
35 } // namespace
37 HidServiceWin::HidServiceWin() {
38 Enumerate();
41 HidServiceWin::~HidServiceWin() {}
43 void HidServiceWin::Enumerate() {
44 BOOL res;
45 HDEVINFO device_info_set;
46 SP_DEVINFO_DATA devinfo_data;
47 SP_DEVICE_INTERFACE_DATA device_interface_data;
49 memset(&devinfo_data, 0, sizeof(SP_DEVINFO_DATA));
50 devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA);
51 device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
53 device_info_set = SetupDiGetClassDevs(
54 &GUID_DEVINTERFACE_HID,
55 NULL,
56 NULL,
57 DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
59 std::set<std::string> connected_devices;
61 if (device_info_set != INVALID_HANDLE_VALUE) {
62 for (int device_index = 0;
63 SetupDiEnumDeviceInterfaces(device_info_set,
64 NULL,
65 &GUID_DEVINTERFACE_HID,
66 device_index,
67 &device_interface_data);
68 ++device_index) {
69 DWORD required_size = 0;
71 // Determime the required size of detail struct.
72 SetupDiGetDeviceInterfaceDetailA(device_info_set,
73 &device_interface_data,
74 NULL,
76 &required_size,
77 NULL);
79 scoped_ptr<SP_DEVICE_INTERFACE_DETAIL_DATA_A, base::FreeDeleter>
80 device_interface_detail_data(
81 static_cast<SP_DEVICE_INTERFACE_DETAIL_DATA_A*>(
82 malloc(required_size)));
83 device_interface_detail_data->cbSize =
84 sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
86 // Get the detailed data for this device.
87 res = SetupDiGetDeviceInterfaceDetailA(device_info_set,
88 &device_interface_data,
89 device_interface_detail_data.get(),
90 required_size,
91 NULL,
92 NULL);
93 if (!res)
94 continue;
96 // Enumerate device info. Looking for Setup Class "HIDClass".
97 for (DWORD i = 0;
98 SetupDiEnumDeviceInfo(device_info_set, i, &devinfo_data);
99 i++) {
100 char class_name[256] = {0};
101 res = SetupDiGetDeviceRegistryPropertyA(device_info_set,
102 &devinfo_data,
103 SPDRP_CLASS,
104 NULL,
105 (PBYTE) class_name,
106 sizeof(class_name) - 1,
107 NULL);
108 if (!res)
109 break;
110 if (memcmp(class_name, kHIDClass, sizeof(kHIDClass)) == 0) {
111 char driver_name[256] = {0};
112 // Get bounded driver.
113 res = SetupDiGetDeviceRegistryPropertyA(device_info_set,
114 &devinfo_data,
115 SPDRP_DRIVER,
116 NULL,
117 (PBYTE) driver_name,
118 sizeof(driver_name) - 1,
119 NULL);
120 if (res) {
121 // Found the driver.
122 break;
127 if (!res)
128 continue;
130 PlatformAddDevice(device_interface_detail_data->DevicePath);
131 connected_devices.insert(device_interface_detail_data->DevicePath);
135 // Find disconnected devices.
136 const DeviceMap& devices = GetDevicesNoEnumerate();
137 std::vector<std::string> disconnected_devices;
138 for (DeviceMap::const_iterator it = devices.begin();
139 it != devices.end();
140 ++it) {
141 if (!ContainsKey(connected_devices, it->first)) {
142 disconnected_devices.push_back(it->first);
146 // Remove disconnected devices.
147 for (size_t i = 0; i < disconnected_devices.size(); ++i) {
148 PlatformRemoveDevice(disconnected_devices[i]);
152 void HidServiceWin::CollectInfoFromButtonCaps(
153 PHIDP_PREPARSED_DATA preparsed_data,
154 HIDP_REPORT_TYPE report_type,
155 USHORT button_caps_length,
156 HidCollectionInfo* collection_info) {
157 if (button_caps_length > 0) {
158 scoped_ptr<HIDP_BUTTON_CAPS[]> button_caps(
159 new HIDP_BUTTON_CAPS[button_caps_length]);
160 if (HidP_GetButtonCaps(report_type,
161 &button_caps[0],
162 &button_caps_length,
163 preparsed_data) == HIDP_STATUS_SUCCESS) {
164 for (size_t i = 0; i < button_caps_length; i++) {
165 int report_id = button_caps[i].ReportID;
166 if (report_id != 0) {
167 collection_info->report_ids.insert(report_id);
174 void HidServiceWin::CollectInfoFromValueCaps(
175 PHIDP_PREPARSED_DATA preparsed_data,
176 HIDP_REPORT_TYPE report_type,
177 USHORT value_caps_length,
178 HidCollectionInfo* collection_info) {
179 if (value_caps_length > 0) {
180 scoped_ptr<HIDP_VALUE_CAPS[]> value_caps(
181 new HIDP_VALUE_CAPS[value_caps_length]);
182 if (HidP_GetValueCaps(
183 report_type, &value_caps[0], &value_caps_length, preparsed_data) ==
184 HIDP_STATUS_SUCCESS) {
185 for (size_t i = 0; i < value_caps_length; i++) {
186 int report_id = value_caps[i].ReportID;
187 if (report_id != 0) {
188 collection_info->report_ids.insert(report_id);
195 void HidServiceWin::PlatformAddDevice(const std::string& device_path) {
196 HidDeviceInfo device_info;
197 device_info.device_id = device_path;
199 // Try to open the device.
200 base::win::ScopedHandle device_handle(
201 CreateFileA(device_path.c_str(),
202 GENERIC_WRITE | GENERIC_READ,
203 FILE_SHARE_READ | FILE_SHARE_WRITE,
204 NULL,
205 OPEN_EXISTING,
206 FILE_FLAG_OVERLAPPED,
207 0));
209 if (!device_handle.IsValid() &&
210 GetLastError() == base::File::FILE_ERROR_ACCESS_DENIED) {
211 base::win::ScopedHandle device_handle(
212 CreateFileA(device_path.c_str(),
213 GENERIC_READ,
214 FILE_SHARE_READ,
215 NULL,
216 OPEN_EXISTING,
217 FILE_FLAG_OVERLAPPED,
218 0));
220 if (!device_handle.IsValid())
221 return;
224 // Get VID/PID pair.
225 HIDD_ATTRIBUTES attrib = {0};
226 attrib.Size = sizeof(HIDD_ATTRIBUTES);
227 if (!HidD_GetAttributes(device_handle.Get(), &attrib))
228 return;
230 device_info.vendor_id = attrib.VendorID;
231 device_info.product_id = attrib.ProductID;
233 for (ULONG i = 32;
234 HidD_SetNumInputBuffers(device_handle.Get(), i);
235 i <<= 1);
237 // Get usage and usage page (optional).
238 PHIDP_PREPARSED_DATA preparsed_data;
239 if (HidD_GetPreparsedData(device_handle.Get(), &preparsed_data) &&
240 preparsed_data) {
241 HIDP_CAPS capabilities = {0};
242 if (HidP_GetCaps(preparsed_data, &capabilities) == HIDP_STATUS_SUCCESS) {
243 device_info.max_input_report_size = capabilities.InputReportByteLength;
244 device_info.max_output_report_size = capabilities.OutputReportByteLength;
245 device_info.max_feature_report_size =
246 capabilities.FeatureReportByteLength;
247 HidCollectionInfo collection_info;
248 collection_info.usage = HidUsageAndPage(
249 capabilities.Usage,
250 static_cast<HidUsageAndPage::Page>(capabilities.UsagePage));
251 CollectInfoFromButtonCaps(preparsed_data,
252 HidP_Input,
253 capabilities.NumberInputButtonCaps,
254 &collection_info);
255 CollectInfoFromButtonCaps(preparsed_data,
256 HidP_Output,
257 capabilities.NumberOutputButtonCaps,
258 &collection_info);
259 CollectInfoFromButtonCaps(preparsed_data,
260 HidP_Feature,
261 capabilities.NumberFeatureButtonCaps,
262 &collection_info);
263 CollectInfoFromValueCaps(preparsed_data,
264 HidP_Input,
265 capabilities.NumberInputValueCaps,
266 &collection_info);
267 CollectInfoFromValueCaps(preparsed_data,
268 HidP_Output,
269 capabilities.NumberOutputValueCaps,
270 &collection_info);
271 CollectInfoFromValueCaps(preparsed_data,
272 HidP_Feature,
273 capabilities.NumberFeatureValueCaps,
274 &collection_info);
275 if (!collection_info.report_ids.empty()) {
276 device_info.has_report_id = true;
278 device_info.collections.push_back(collection_info);
280 // Whether or not the device includes report IDs in its reports the size
281 // of the report ID is included in the value provided by Windows. This
282 // appears contrary to the MSDN documentation.
283 if (device_info.max_input_report_size > 0) {
284 device_info.max_input_report_size--;
286 if (device_info.max_output_report_size > 0) {
287 device_info.max_output_report_size--;
289 if (device_info.max_feature_report_size > 0) {
290 device_info.max_feature_report_size--;
292 HidD_FreePreparsedData(preparsed_data);
295 AddDevice(device_info);
298 void HidServiceWin::PlatformRemoveDevice(const std::string& device_path) {
299 RemoveDevice(device_path);
302 void HidServiceWin::GetDevices(std::vector<HidDeviceInfo>* devices) {
303 Enumerate();
304 HidService::GetDevices(devices);
307 scoped_refptr<HidConnection> HidServiceWin::Connect(
308 const HidDeviceId& device_id) {
309 HidDeviceInfo device_info;
310 if (!GetDeviceInfo(device_id, &device_info))
311 return NULL;
312 scoped_refptr<HidConnectionWin> connection(new HidConnectionWin(device_info));
313 if (!connection->available()) {
314 PLOG(ERROR) << "Failed to open device.";
315 return NULL;
317 return connection;
320 } // namespace device