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"
10 #include "base/files/file.h"
11 #include "base/location.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/stl_util.h"
14 #include "base/strings/sys_string_conversions.h"
15 #include "base/thread_task_runner_handle.h"
16 #include "base/threading/thread_restrictions.h"
17 #include "device/hid/hid_connection_win.h"
18 #include "device/hid/hid_device_info.h"
19 #include "net/base/io_buffer.h"
27 #include "base/win/scoped_handle.h"
29 #endif // defined(OS_WIN)
31 // Setup API is required to enumerate HID devices.
32 #pragma comment(lib, "setupapi.lib")
33 #pragma comment(lib, "hid.lib")
38 const char kHIDClass
[] = "HIDClass";
42 HidServiceWin::HidServiceWin() {
43 base::ThreadRestrictions::AssertIOAllowed();
44 task_runner_
= base::ThreadTaskRunnerHandle::Get();
45 DCHECK(task_runner_
.get());
49 HidServiceWin::~HidServiceWin() {}
51 void HidServiceWin::Enumerate() {
53 HDEVINFO device_info_set
;
54 SP_DEVINFO_DATA devinfo_data
;
55 SP_DEVICE_INTERFACE_DATA device_interface_data
;
57 memset(&devinfo_data
, 0, sizeof(SP_DEVINFO_DATA
));
58 devinfo_data
.cbSize
= sizeof(SP_DEVINFO_DATA
);
59 device_interface_data
.cbSize
= sizeof(SP_DEVICE_INTERFACE_DATA
);
61 device_info_set
= SetupDiGetClassDevs(
62 &GUID_DEVINTERFACE_HID
,
65 DIGCF_PRESENT
| DIGCF_DEVICEINTERFACE
);
67 std::set
<std::string
> connected_devices
;
69 if (device_info_set
!= INVALID_HANDLE_VALUE
) {
70 for (int device_index
= 0;
71 SetupDiEnumDeviceInterfaces(device_info_set
,
73 &GUID_DEVINTERFACE_HID
,
75 &device_interface_data
);
77 DWORD required_size
= 0;
79 // Determime the required size of detail struct.
80 SetupDiGetDeviceInterfaceDetailA(device_info_set
,
81 &device_interface_data
,
87 scoped_ptr
<SP_DEVICE_INTERFACE_DETAIL_DATA_A
, base::FreeDeleter
>
88 device_interface_detail_data(
89 static_cast<SP_DEVICE_INTERFACE_DETAIL_DATA_A
*>(
90 malloc(required_size
)));
91 device_interface_detail_data
->cbSize
=
92 sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A
);
94 // Get the detailed data for this device.
95 res
= SetupDiGetDeviceInterfaceDetailA(device_info_set
,
96 &device_interface_data
,
97 device_interface_detail_data
.get(),
104 // Enumerate device info. Looking for Setup Class "HIDClass".
106 SetupDiEnumDeviceInfo(device_info_set
, i
, &devinfo_data
);
108 char class_name
[256] = {0};
109 res
= SetupDiGetDeviceRegistryPropertyA(device_info_set
,
114 sizeof(class_name
) - 1,
118 if (memcmp(class_name
, kHIDClass
, sizeof(kHIDClass
)) == 0) {
119 char driver_name
[256] = {0};
120 // Get bounded driver.
121 res
= SetupDiGetDeviceRegistryPropertyA(device_info_set
,
126 sizeof(driver_name
) - 1,
138 PlatformAddDevice(device_interface_detail_data
->DevicePath
);
139 connected_devices
.insert(device_interface_detail_data
->DevicePath
);
143 // Find disconnected devices.
144 std::vector
<std::string
> disconnected_devices
;
145 for (DeviceMap::const_iterator it
= devices().begin(); it
!= devices().end();
147 if (!ContainsKey(connected_devices
, it
->first
)) {
148 disconnected_devices
.push_back(it
->first
);
152 // Remove disconnected devices.
153 for (size_t i
= 0; i
< disconnected_devices
.size(); ++i
) {
154 PlatformRemoveDevice(disconnected_devices
[i
]);
158 void HidServiceWin::CollectInfoFromButtonCaps(
159 PHIDP_PREPARSED_DATA preparsed_data
,
160 HIDP_REPORT_TYPE report_type
,
161 USHORT button_caps_length
,
162 HidCollectionInfo
* collection_info
) {
163 if (button_caps_length
> 0) {
164 scoped_ptr
<HIDP_BUTTON_CAPS
[]> button_caps(
165 new HIDP_BUTTON_CAPS
[button_caps_length
]);
166 if (HidP_GetButtonCaps(report_type
,
169 preparsed_data
) == HIDP_STATUS_SUCCESS
) {
170 for (size_t i
= 0; i
< button_caps_length
; i
++) {
171 int report_id
= button_caps
[i
].ReportID
;
172 if (report_id
!= 0) {
173 collection_info
->report_ids
.insert(report_id
);
180 void HidServiceWin::CollectInfoFromValueCaps(
181 PHIDP_PREPARSED_DATA preparsed_data
,
182 HIDP_REPORT_TYPE report_type
,
183 USHORT value_caps_length
,
184 HidCollectionInfo
* collection_info
) {
185 if (value_caps_length
> 0) {
186 scoped_ptr
<HIDP_VALUE_CAPS
[]> value_caps(
187 new HIDP_VALUE_CAPS
[value_caps_length
]);
188 if (HidP_GetValueCaps(
189 report_type
, &value_caps
[0], &value_caps_length
, preparsed_data
) ==
190 HIDP_STATUS_SUCCESS
) {
191 for (size_t i
= 0; i
< value_caps_length
; i
++) {
192 int report_id
= value_caps
[i
].ReportID
;
193 if (report_id
!= 0) {
194 collection_info
->report_ids
.insert(report_id
);
201 void HidServiceWin::PlatformAddDevice(const std::string
& device_path
) {
202 HidDeviceInfo device_info
;
203 device_info
.device_id
= device_path
;
205 // Try to open the device.
206 base::win::ScopedHandle
device_handle(
207 CreateFileA(device_path
.c_str(),
208 GENERIC_WRITE
| GENERIC_READ
,
209 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
212 FILE_FLAG_OVERLAPPED
,
215 if (!device_handle
.IsValid() &&
216 GetLastError() == base::File::FILE_ERROR_ACCESS_DENIED
) {
217 base::win::ScopedHandle
device_handle(
218 CreateFileA(device_path
.c_str(),
223 FILE_FLAG_OVERLAPPED
,
226 if (!device_handle
.IsValid())
231 HIDD_ATTRIBUTES attrib
= {0};
232 attrib
.Size
= sizeof(HIDD_ATTRIBUTES
);
233 if (!HidD_GetAttributes(device_handle
.Get(), &attrib
))
236 device_info
.vendor_id
= attrib
.VendorID
;
237 device_info
.product_id
= attrib
.ProductID
;
240 HidD_SetNumInputBuffers(device_handle
.Get(), i
);
243 // Get usage and usage page (optional).
244 PHIDP_PREPARSED_DATA preparsed_data
;
245 if (HidD_GetPreparsedData(device_handle
.Get(), &preparsed_data
) &&
247 HIDP_CAPS capabilities
= {0};
248 if (HidP_GetCaps(preparsed_data
, &capabilities
) == HIDP_STATUS_SUCCESS
) {
249 device_info
.max_input_report_size
= capabilities
.InputReportByteLength
;
250 device_info
.max_output_report_size
= capabilities
.OutputReportByteLength
;
251 device_info
.max_feature_report_size
=
252 capabilities
.FeatureReportByteLength
;
253 HidCollectionInfo collection_info
;
254 collection_info
.usage
= HidUsageAndPage(
256 static_cast<HidUsageAndPage::Page
>(capabilities
.UsagePage
));
257 CollectInfoFromButtonCaps(preparsed_data
,
259 capabilities
.NumberInputButtonCaps
,
261 CollectInfoFromButtonCaps(preparsed_data
,
263 capabilities
.NumberOutputButtonCaps
,
265 CollectInfoFromButtonCaps(preparsed_data
,
267 capabilities
.NumberFeatureButtonCaps
,
269 CollectInfoFromValueCaps(preparsed_data
,
271 capabilities
.NumberInputValueCaps
,
273 CollectInfoFromValueCaps(preparsed_data
,
275 capabilities
.NumberOutputValueCaps
,
277 CollectInfoFromValueCaps(preparsed_data
,
279 capabilities
.NumberFeatureValueCaps
,
281 if (!collection_info
.report_ids
.empty()) {
282 device_info
.has_report_id
= true;
284 device_info
.collections
.push_back(collection_info
);
286 // Whether or not the device includes report IDs in its reports the size
287 // of the report ID is included in the value provided by Windows. This
288 // appears contrary to the MSDN documentation.
289 if (device_info
.max_input_report_size
> 0) {
290 device_info
.max_input_report_size
--;
292 if (device_info
.max_output_report_size
> 0) {
293 device_info
.max_output_report_size
--;
295 if (device_info
.max_feature_report_size
> 0) {
296 device_info
.max_feature_report_size
--;
298 HidD_FreePreparsedData(preparsed_data
);
301 AddDevice(device_info
);
304 void HidServiceWin::PlatformRemoveDevice(const std::string
& device_path
) {
305 RemoveDevice(device_path
);
308 void HidServiceWin::GetDevices(std::vector
<HidDeviceInfo
>* devices
) {
310 HidService::GetDevices(devices
);
313 void HidServiceWin::Connect(const HidDeviceId
& device_id
,
314 const ConnectCallback
& callback
) {
315 DCHECK(thread_checker_
.CalledOnValidThread());
316 const auto& map_entry
= devices().find(device_id
);
317 if (map_entry
== devices().end()) {
318 task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, nullptr));
321 const HidDeviceInfo
& device_info
= map_entry
->second
;
323 scoped_refptr
<HidConnectionWin
> connection(new HidConnectionWin(device_info
));
324 if (!connection
->available()) {
325 PLOG(ERROR
) << "Failed to open device";
326 connection
= nullptr;
328 task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, connection
));
331 } // namespace device