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"
13 #include "base/bind.h"
14 #include "base/files/file.h"
15 #include "base/location.h"
16 #include "base/single_thread_task_runner.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/sys_string_conversions.h"
19 #include "base/thread_task_runner_handle.h"
20 #include "base/threading/thread_restrictions.h"
21 #include "components/device_event_log/device_event_log.h"
22 #include "device/hid/hid_connection_win.h"
23 #include "device/hid/hid_device_info.h"
24 #include "net/base/io_buffer.h"
26 // Setup API is required to enumerate HID devices.
27 #pragma comment(lib, "setupapi.lib")
28 #pragma comment(lib, "hid.lib")
35 // This function does nothing.
39 HidServiceWin::HidServiceWin(
40 scoped_refptr
<base::SingleThreadTaskRunner
> file_task_runner
)
41 : device_observer_(this),
42 file_task_runner_(file_task_runner
),
44 task_runner_
= base::ThreadTaskRunnerHandle::Get();
45 DCHECK(task_runner_
.get());
46 DeviceMonitorWin
* device_monitor
=
47 DeviceMonitorWin::GetForDeviceInterface(GUID_DEVINTERFACE_HID
);
49 device_observer_
.Add(device_monitor
);
51 file_task_runner_
->PostTask(
52 FROM_HERE
, base::Bind(&HidServiceWin::EnumerateOnFileThread
,
53 weak_factory_
.GetWeakPtr(), task_runner_
));
56 void HidServiceWin::Connect(const HidDeviceId
& device_id
,
57 const ConnectCallback
& callback
) {
58 DCHECK(thread_checker_
.CalledOnValidThread());
59 const auto& map_entry
= devices().find(device_id
);
60 if (map_entry
== devices().end()) {
61 task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, nullptr));
64 scoped_refptr
<HidDeviceInfo
> device_info
= map_entry
->second
;
66 base::win::ScopedHandle
file(OpenDevice(device_info
->device_id()));
67 if (!file
.IsValid()) {
68 HID_PLOG(EVENT
) << "Failed to open device";
69 task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, nullptr));
73 task_runner_
->PostTask(
75 base::Bind(callback
, new HidConnectionWin(device_info
, file
.Pass())));
78 HidServiceWin::~HidServiceWin() {
82 void HidServiceWin::EnumerateOnFileThread(
83 base::WeakPtr
<HidServiceWin
> service
,
84 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner
) {
85 HDEVINFO device_info_set
=
86 SetupDiGetClassDevs(&GUID_DEVINTERFACE_HID
, NULL
, NULL
,
87 DIGCF_PRESENT
| DIGCF_DEVICEINTERFACE
);
89 if (device_info_set
!= INVALID_HANDLE_VALUE
) {
90 SP_DEVICE_INTERFACE_DATA device_interface_data
;
91 device_interface_data
.cbSize
= sizeof(SP_DEVICE_INTERFACE_DATA
);
93 for (int device_index
= 0;
94 SetupDiEnumDeviceInterfaces(device_info_set
,
96 &GUID_DEVINTERFACE_HID
,
98 &device_interface_data
);
100 DWORD required_size
= 0;
102 // Determime the required size of detail struct.
103 SetupDiGetDeviceInterfaceDetail(device_info_set
, &device_interface_data
,
104 NULL
, 0, &required_size
, NULL
);
106 scoped_ptr
<SP_DEVICE_INTERFACE_DETAIL_DATA
, base::FreeDeleter
>
107 device_interface_detail_data(
108 static_cast<SP_DEVICE_INTERFACE_DETAIL_DATA
*>(malloc(required_size
)));
109 device_interface_detail_data
->cbSize
=
110 sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA
);
112 // Get the detailed data for this device.
113 BOOL res
= SetupDiGetDeviceInterfaceDetail(
114 device_info_set
, &device_interface_data
,
115 device_interface_detail_data
.get(), required_size
, NULL
, NULL
);
120 std::string
device_path(
121 base::SysWideToUTF8(device_interface_detail_data
->DevicePath
));
122 DCHECK(base::IsStringASCII(device_path
));
123 AddDeviceOnFileThread(service
, task_runner
,
124 base::StringToLowerASCII(device_path
));
128 task_runner
->PostTask(
129 FROM_HERE
, base::Bind(&HidServiceWin::FirstEnumerationComplete
, service
));
133 void HidServiceWin::CollectInfoFromButtonCaps(
134 PHIDP_PREPARSED_DATA preparsed_data
,
135 HIDP_REPORT_TYPE report_type
,
136 USHORT button_caps_length
,
137 HidCollectionInfo
* collection_info
) {
138 if (button_caps_length
> 0) {
139 scoped_ptr
<HIDP_BUTTON_CAPS
[]> button_caps(
140 new HIDP_BUTTON_CAPS
[button_caps_length
]);
141 if (HidP_GetButtonCaps(report_type
,
144 preparsed_data
) == HIDP_STATUS_SUCCESS
) {
145 for (size_t i
= 0; i
< button_caps_length
; i
++) {
146 int report_id
= button_caps
[i
].ReportID
;
147 if (report_id
!= 0) {
148 collection_info
->report_ids
.insert(report_id
);
156 void HidServiceWin::CollectInfoFromValueCaps(
157 PHIDP_PREPARSED_DATA preparsed_data
,
158 HIDP_REPORT_TYPE report_type
,
159 USHORT value_caps_length
,
160 HidCollectionInfo
* collection_info
) {
161 if (value_caps_length
> 0) {
162 scoped_ptr
<HIDP_VALUE_CAPS
[]> value_caps(
163 new HIDP_VALUE_CAPS
[value_caps_length
]);
164 if (HidP_GetValueCaps(
165 report_type
, &value_caps
[0], &value_caps_length
, preparsed_data
) ==
166 HIDP_STATUS_SUCCESS
) {
167 for (size_t i
= 0; i
< value_caps_length
; i
++) {
168 int report_id
= value_caps
[i
].ReportID
;
169 if (report_id
!= 0) {
170 collection_info
->report_ids
.insert(report_id
);
178 void HidServiceWin::AddDeviceOnFileThread(
179 base::WeakPtr
<HidServiceWin
> service
,
180 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner
,
181 const std::string
& device_path
) {
182 base::win::ScopedHandle
device_handle(OpenDevice(device_path
));
183 if (!device_handle
.IsValid()) {
187 HIDD_ATTRIBUTES attrib
= {0};
188 attrib
.Size
= sizeof(HIDD_ATTRIBUTES
);
189 if (!HidD_GetAttributes(device_handle
.Get(), &attrib
)) {
190 HID_LOG(EVENT
) << "Failed to get device attributes.";
194 PHIDP_PREPARSED_DATA preparsed_data
= nullptr;
195 if (!HidD_GetPreparsedData(device_handle
.Get(), &preparsed_data
) ||
197 HID_LOG(EVENT
) << "Failed to get device data.";
201 HIDP_CAPS capabilities
= {0};
202 if (HidP_GetCaps(preparsed_data
, &capabilities
) != HIDP_STATUS_SUCCESS
) {
203 HID_LOG(EVENT
) << "Failed to get device capabilities.";
204 HidD_FreePreparsedData(preparsed_data
);
208 // Whether or not the device includes report IDs in its reports the size
209 // of the report ID is included in the value provided by Windows. This
210 // appears contrary to the MSDN documentation.
211 size_t max_input_report_size
= 0;
212 size_t max_output_report_size
= 0;
213 size_t max_feature_report_size
= 0;
214 if (capabilities
.InputReportByteLength
> 0) {
215 max_input_report_size
= capabilities
.InputReportByteLength
- 1;
217 if (capabilities
.OutputReportByteLength
> 0) {
218 max_output_report_size
= capabilities
.OutputReportByteLength
- 1;
220 if (capabilities
.FeatureReportByteLength
> 0) {
221 max_feature_report_size
= capabilities
.FeatureReportByteLength
- 1;
224 HidCollectionInfo collection_info
;
225 collection_info
.usage
= HidUsageAndPage(
227 static_cast<HidUsageAndPage::Page
>(capabilities
.UsagePage
));
228 CollectInfoFromButtonCaps(preparsed_data
, HidP_Input
,
229 capabilities
.NumberInputButtonCaps
,
231 CollectInfoFromButtonCaps(preparsed_data
, HidP_Output
,
232 capabilities
.NumberOutputButtonCaps
,
234 CollectInfoFromButtonCaps(preparsed_data
, HidP_Feature
,
235 capabilities
.NumberFeatureButtonCaps
,
237 CollectInfoFromValueCaps(preparsed_data
, HidP_Input
,
238 capabilities
.NumberInputValueCaps
, &collection_info
);
239 CollectInfoFromValueCaps(preparsed_data
, HidP_Output
,
240 capabilities
.NumberOutputValueCaps
,
242 CollectInfoFromValueCaps(preparsed_data
, HidP_Feature
,
243 capabilities
.NumberFeatureValueCaps
,
246 // 1023 characters plus NULL terminator is more than enough for a USB string
247 // descriptor which is limited to 126 characters.
248 wchar_t buffer
[1024];
249 std::string product_name
;
250 if (HidD_GetProductString(device_handle
.Get(), &buffer
[0], sizeof(buffer
))) {
251 // NULL termination guaranteed by the API.
252 product_name
= base::SysWideToUTF8(buffer
);
254 std::string serial_number
;
255 if (HidD_GetSerialNumberString(device_handle
.Get(), &buffer
[0],
257 // NULL termination guaranteed by the API.
258 serial_number
= base::SysWideToUTF8(buffer
);
261 // This populates the HidDeviceInfo instance without a raw report descriptor.
262 // The descriptor is unavailable on Windows because HID devices are exposed to
263 // user-space as individual top-level collections.
264 scoped_refptr
<HidDeviceInfo
> device_info(new HidDeviceInfo(
265 device_path
, attrib
.VendorID
, attrib
.ProductID
, product_name
,
267 kHIDBusTypeUSB
, // TODO(reillyg): Detect Bluetooth. crbug.com/443335
268 collection_info
, max_input_report_size
, max_output_report_size
,
269 max_feature_report_size
));
271 HidD_FreePreparsedData(preparsed_data
);
272 task_runner
->PostTask(
273 FROM_HERE
, base::Bind(&HidServiceWin::AddDevice
, service
, device_info
));
276 void HidServiceWin::OnDeviceAdded(const GUID
& class_guid
,
277 const std::string
& device_path
) {
278 file_task_runner_
->PostTask(
280 base::Bind(&HidServiceWin::AddDeviceOnFileThread
,
281 weak_factory_
.GetWeakPtr(), task_runner_
, device_path
));
284 void HidServiceWin::OnDeviceRemoved(const GUID
& class_guid
,
285 const std::string
& device_path
) {
286 // Execute a no-op closure on the file task runner to synchronize with any
287 // devices that are still being enumerated.
288 file_task_runner_
->PostTaskAndReply(
289 FROM_HERE
, base::Bind(&Noop
),
290 base::Bind(&HidServiceWin::RemoveDevice
, weak_factory_
.GetWeakPtr(),
295 base::win::ScopedHandle
HidServiceWin::OpenDevice(
296 const std::string
& device_path
) {
297 base::win::ScopedHandle
file(
298 CreateFileA(device_path
.c_str(), GENERIC_WRITE
| GENERIC_READ
,
299 FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
,
300 FILE_FLAG_OVERLAPPED
, NULL
));
301 if (!file
.IsValid() &&
302 GetLastError() == base::File::FILE_ERROR_ACCESS_DENIED
) {
303 file
.Set(CreateFileA(device_path
.c_str(), GENERIC_READ
, FILE_SHARE_READ
,
304 NULL
, OPEN_EXISTING
, FILE_FLAG_OVERLAPPED
, NULL
));
309 } // namespace device