Make sure GN deps rolls include the optional GN bots.
[chromium-blink-merge.git] / device / hid / hid_service_win.cc
blob627cc01e79b9d8b826548172f3c3035209318ca8
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 #define INITGUID
9 #include <dbt.h>
10 #include <setupapi.h>
11 #include <winioctl.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")
30 namespace device {
32 namespace {
34 void Noop() {
35 // This function does nothing.
39 HidServiceWin::HidServiceWin(
40 scoped_refptr<base::SingleThreadTaskRunner> file_task_runner)
41 : file_task_runner_(file_task_runner),
42 device_observer_(this),
43 weak_factory_(this) {
44 task_runner_ = base::ThreadTaskRunnerHandle::Get();
45 DCHECK(task_runner_.get());
46 DeviceMonitorWin* device_monitor =
47 DeviceMonitorWin::GetForDeviceInterface(GUID_DEVINTERFACE_HID);
48 if (device_monitor) {
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));
62 return;
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));
70 return;
73 task_runner_->PostTask(
74 FROM_HERE,
75 base::Bind(callback, new HidConnectionWin(device_info, file.Pass())));
78 HidServiceWin::~HidServiceWin() {
81 // static
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,
95 NULL,
96 &GUID_DEVINTERFACE_HID,
97 device_index,
98 &device_interface_data);
99 ++device_index) {
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);
116 if (!res) {
117 continue;
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));
132 // static
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,
142 &button_caps[0],
143 &button_caps_length,
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);
155 // static
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);
177 // static
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()) {
184 return;
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.";
191 return;
194 PHIDP_PREPARSED_DATA preparsed_data = nullptr;
195 if (!HidD_GetPreparsedData(device_handle.Get(), &preparsed_data) ||
196 !preparsed_data) {
197 HID_LOG(EVENT) << "Failed to get device data.";
198 return;
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);
205 return;
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(
226 capabilities.Usage,
227 static_cast<HidUsageAndPage::Page>(capabilities.UsagePage));
228 CollectInfoFromButtonCaps(preparsed_data, HidP_Input,
229 capabilities.NumberInputButtonCaps,
230 &collection_info);
231 CollectInfoFromButtonCaps(preparsed_data, HidP_Output,
232 capabilities.NumberOutputButtonCaps,
233 &collection_info);
234 CollectInfoFromButtonCaps(preparsed_data, HidP_Feature,
235 capabilities.NumberFeatureButtonCaps,
236 &collection_info);
237 CollectInfoFromValueCaps(preparsed_data, HidP_Input,
238 capabilities.NumberInputValueCaps, &collection_info);
239 CollectInfoFromValueCaps(preparsed_data, HidP_Output,
240 capabilities.NumberOutputValueCaps,
241 &collection_info);
242 CollectInfoFromValueCaps(preparsed_data, HidP_Feature,
243 capabilities.NumberFeatureValueCaps,
244 &collection_info);
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],
256 sizeof(buffer))) {
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,
266 serial_number,
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(
279 FROM_HERE,
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(),
291 device_path));
294 // static
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));
306 return file.Pass();
309 } // namespace device