OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "device/hid/hid_service_win.h" | 5 #include "device/hid/hid_service_win.h" |
6 | 6 |
7 #include <cstdlib> | 7 #define INITGUID |
8 | |
9 #include <dbt.h> | |
10 #include <setupapi.h> | |
11 #include <winioctl.h> | |
8 | 12 |
9 #include "base/bind.h" | 13 #include "base/bind.h" |
10 #include "base/files/file.h" | 14 #include "base/files/file.h" |
11 #include "base/location.h" | 15 #include "base/location.h" |
12 #include "base/single_thread_task_runner.h" | 16 #include "base/single_thread_task_runner.h" |
13 #include "base/stl_util.h" | 17 #include "base/strings/string_util.h" |
14 #include "base/strings/sys_string_conversions.h" | 18 #include "base/strings/sys_string_conversions.h" |
15 #include "base/thread_task_runner_handle.h" | 19 #include "base/thread_task_runner_handle.h" |
16 #include "base/threading/thread_restrictions.h" | 20 #include "base/threading/thread_restrictions.h" |
21 #include "base/win/message_window.h" | |
17 #include "device/hid/hid_connection_win.h" | 22 #include "device/hid/hid_connection_win.h" |
18 #include "device/hid/hid_device_info.h" | 23 #include "device/hid/hid_device_info.h" |
19 #include "net/base/io_buffer.h" | 24 #include "net/base/io_buffer.h" |
20 | 25 |
21 #if defined(OS_WIN) | |
22 | |
23 #define INITGUID | |
24 | |
25 #include <setupapi.h> | |
26 #include <winioctl.h> | |
27 #include "base/win/scoped_handle.h" | |
28 | |
29 #endif // defined(OS_WIN) | |
30 | |
31 // Setup API is required to enumerate HID devices. | 26 // Setup API is required to enumerate HID devices. |
32 #pragma comment(lib, "setupapi.lib") | 27 #pragma comment(lib, "setupapi.lib") |
33 #pragma comment(lib, "hid.lib") | 28 #pragma comment(lib, "hid.lib") |
34 | 29 |
35 namespace device { | 30 namespace device { |
36 namespace { | 31 namespace { |
37 | 32 |
38 const char kHIDClass[] = "HIDClass"; | 33 const char kHIDClass[] = "HIDClass"; |
34 const wchar_t kWindowClassName[] = L"HidServiceMessageWindow"; | |
39 | 35 |
40 } // namespace | 36 } // namespace |
41 | 37 |
42 HidServiceWin::HidServiceWin() { | 38 HidServiceWin::HidServiceWin() { |
43 task_runner_ = base::ThreadTaskRunnerHandle::Get(); | 39 task_runner_ = base::ThreadTaskRunnerHandle::Get(); |
44 DCHECK(task_runner_.get()); | 40 DCHECK(task_runner_.get()); |
45 Enumerate(); | 41 RegisterForDeviceNotifications(); |
42 DoInitialEnumeration(); | |
46 } | 43 } |
47 | 44 |
48 HidServiceWin::~HidServiceWin() {} | 45 void HidServiceWin::Connect(const HidDeviceId& device_id, |
46 const ConnectCallback& callback) { | |
47 DCHECK(thread_checker_.CalledOnValidThread()); | |
48 const auto& map_entry = devices().find(device_id); | |
49 if (map_entry == devices().end()) { | |
50 task_runner_->PostTask(FROM_HERE, base::Bind(callback, nullptr)); | |
51 return; | |
52 } | |
53 const HidDeviceInfo& device_info = map_entry->second; | |
49 | 54 |
50 void HidServiceWin::Enumerate() { | 55 base::win::ScopedHandle file(OpenDevice(device_info.device_id)); |
51 BOOL res; | 56 if (!file.IsValid()) { |
52 HDEVINFO device_info_set; | 57 PLOG(ERROR) << "Failed to open device"; |
53 SP_DEVINFO_DATA devinfo_data; | 58 task_runner_->PostTask(FROM_HERE, base::Bind(callback, nullptr)); |
54 SP_DEVICE_INTERFACE_DATA device_interface_data; | 59 return; |
60 } | |
55 | 61 |
56 memset(&devinfo_data, 0, sizeof(SP_DEVINFO_DATA)); | 62 task_runner_->PostTask( |
57 devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA); | 63 FROM_HERE, |
58 device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); | 64 base::Bind(callback, new HidConnectionWin(device_info, file.Pass()))); |
65 } | |
59 | 66 |
60 device_info_set = SetupDiGetClassDevs( | 67 HidServiceWin::~HidServiceWin() { |
61 &GUID_DEVINTERFACE_HID, | 68 if (notify_handle_) { |
62 NULL, | 69 UnregisterDeviceNotification(notify_handle_); |
63 NULL, | 70 } |
64 DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); | 71 } |
65 | 72 |
66 std::set<std::string> connected_devices; | 73 void HidServiceWin::RegisterForDeviceNotifications() { |
74 window_.reset(new base::win::MessageWindow()); | |
Ken Rockot(use gerrit already)
2014/12/05 23:27:53
nit: Maybe file a bug + add a TODO which expresses
| |
75 if (!window_->CreateNamed( | |
76 base::Bind(&HidServiceWin::HandleMessage, base::Unretained(this)), | |
77 base::string16(kWindowClassName))) { | |
78 LOG(ERROR) << "Failed to create message window: " << kWindowClassName; | |
79 window_.reset(); | |
80 } | |
81 DEV_BROADCAST_DEVICEINTERFACE db = { sizeof(DEV_BROADCAST_DEVICEINTERFACE), | |
82 DBT_DEVTYP_DEVICEINTERFACE, | |
83 0, | |
84 GUID_DEVINTERFACE_HID }; | |
85 notify_handle_ = RegisterDeviceNotification(window_->hwnd(), &db, | |
86 DEVICE_NOTIFY_WINDOW_HANDLE); | |
87 if (!notify_handle_) { | |
88 LOG(ERROR) << "Failed to register for device notifications."; | |
89 window_.reset(); | |
90 } | |
91 } | |
92 | |
93 bool HidServiceWin::HandleMessage(UINT message, | |
94 WPARAM wparam, | |
95 LPARAM lparam, | |
96 LRESULT* result) { | |
97 if (message == WM_DEVICECHANGE) { | |
98 DEV_BROADCAST_DEVICEINTERFACE* db = | |
99 reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(lparam); | |
100 std::string device_path(base::SysWideToUTF8(db->dbcc_name)); | |
101 DCHECK(base::IsStringASCII(device_path)); | |
102 if (wparam == DBT_DEVICEARRIVAL) { | |
103 PlatformAddDevice(base::StringToLowerASCII(device_path)); | |
104 } else if (wparam == DBT_DEVICEREMOVECOMPLETE) { | |
105 PlatformRemoveDevice(base::StringToLowerASCII(device_path)); | |
106 } | |
107 *result = NULL; | |
108 return true; | |
109 } | |
110 return false; | |
111 } | |
112 | |
113 void HidServiceWin::DoInitialEnumeration() { | |
114 HDEVINFO device_info_set = | |
115 SetupDiGetClassDevs(&GUID_DEVINTERFACE_HID, NULL, NULL, | |
116 DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); | |
67 | 117 |
68 if (device_info_set != INVALID_HANDLE_VALUE) { | 118 if (device_info_set != INVALID_HANDLE_VALUE) { |
119 SP_DEVICE_INTERFACE_DATA device_interface_data; | |
120 device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); | |
121 | |
69 for (int device_index = 0; | 122 for (int device_index = 0; |
70 SetupDiEnumDeviceInterfaces(device_info_set, | 123 SetupDiEnumDeviceInterfaces(device_info_set, |
71 NULL, | 124 NULL, |
72 &GUID_DEVINTERFACE_HID, | 125 &GUID_DEVINTERFACE_HID, |
73 device_index, | 126 device_index, |
74 &device_interface_data); | 127 &device_interface_data); |
75 ++device_index) { | 128 ++device_index) { |
76 DWORD required_size = 0; | 129 DWORD required_size = 0; |
77 | 130 |
78 // Determime the required size of detail struct. | 131 // Determime the required size of detail struct. |
79 SetupDiGetDeviceInterfaceDetailA(device_info_set, | 132 SetupDiGetDeviceInterfaceDetail(device_info_set, &device_interface_data, |
80 &device_interface_data, | 133 NULL, 0, &required_size, NULL); |
81 NULL, | |
82 0, | |
83 &required_size, | |
84 NULL); | |
85 | 134 |
86 scoped_ptr<SP_DEVICE_INTERFACE_DETAIL_DATA_A, base::FreeDeleter> | 135 scoped_ptr<SP_DEVICE_INTERFACE_DETAIL_DATA, base::FreeDeleter> |
87 device_interface_detail_data( | 136 device_interface_detail_data( |
88 static_cast<SP_DEVICE_INTERFACE_DETAIL_DATA_A*>( | 137 static_cast<SP_DEVICE_INTERFACE_DETAIL_DATA*>(malloc(required_size))); |
89 malloc(required_size))); | |
90 device_interface_detail_data->cbSize = | 138 device_interface_detail_data->cbSize = |
91 sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); | 139 sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); |
92 | 140 |
93 // Get the detailed data for this device. | 141 // Get the detailed data for this device. |
94 res = SetupDiGetDeviceInterfaceDetailA(device_info_set, | 142 BOOL res = SetupDiGetDeviceInterfaceDetail( |
95 &device_interface_data, | 143 device_info_set, &device_interface_data, |
96 device_interface_detail_data.get(), | 144 device_interface_detail_data.get(), required_size, NULL, NULL); |
97 required_size, | 145 if (!res) { |
98 NULL, | |
99 NULL); | |
100 if (!res) | |
101 continue; | 146 continue; |
102 | |
103 // Enumerate device info. Looking for Setup Class "HIDClass". | |
104 for (DWORD i = 0; | |
105 SetupDiEnumDeviceInfo(device_info_set, i, &devinfo_data); | |
106 i++) { | |
107 char class_name[256] = {0}; | |
108 res = SetupDiGetDeviceRegistryPropertyA(device_info_set, | |
109 &devinfo_data, | |
110 SPDRP_CLASS, | |
111 NULL, | |
112 (PBYTE) class_name, | |
113 sizeof(class_name) - 1, | |
114 NULL); | |
115 if (!res) | |
116 break; | |
117 if (memcmp(class_name, kHIDClass, sizeof(kHIDClass)) == 0) { | |
118 char driver_name[256] = {0}; | |
119 // Get bounded driver. | |
120 res = SetupDiGetDeviceRegistryPropertyA(device_info_set, | |
121 &devinfo_data, | |
122 SPDRP_DRIVER, | |
123 NULL, | |
124 (PBYTE) driver_name, | |
125 sizeof(driver_name) - 1, | |
126 NULL); | |
127 if (res) { | |
128 // Found the driver. | |
129 break; | |
130 } | |
131 } | |
132 } | 147 } |
133 | 148 |
134 if (!res) | 149 std::string device_path( |
135 continue; | 150 base::SysWideToUTF8(device_interface_detail_data->DevicePath)); |
136 | 151 DCHECK(base::IsStringASCII(device_path)); |
137 PlatformAddDevice(device_interface_detail_data->DevicePath); | 152 PlatformAddDevice(device_path); |
138 connected_devices.insert(device_interface_detail_data->DevicePath); | |
139 } | 153 } |
140 } | 154 } |
141 | |
142 // Find disconnected devices. | |
143 std::vector<std::string> disconnected_devices; | |
144 for (DeviceMap::const_iterator it = devices().begin(); it != devices().end(); | |
145 ++it) { | |
146 if (!ContainsKey(connected_devices, it->first)) { | |
147 disconnected_devices.push_back(it->first); | |
148 } | |
149 } | |
150 | |
151 // Remove disconnected devices. | |
152 for (size_t i = 0; i < disconnected_devices.size(); ++i) { | |
153 PlatformRemoveDevice(disconnected_devices[i]); | |
154 } | |
155 } | 155 } |
156 | 156 |
157 // static | |
157 void HidServiceWin::CollectInfoFromButtonCaps( | 158 void HidServiceWin::CollectInfoFromButtonCaps( |
158 PHIDP_PREPARSED_DATA preparsed_data, | 159 PHIDP_PREPARSED_DATA preparsed_data, |
159 HIDP_REPORT_TYPE report_type, | 160 HIDP_REPORT_TYPE report_type, |
160 USHORT button_caps_length, | 161 USHORT button_caps_length, |
161 HidCollectionInfo* collection_info) { | 162 HidCollectionInfo* collection_info) { |
162 if (button_caps_length > 0) { | 163 if (button_caps_length > 0) { |
163 scoped_ptr<HIDP_BUTTON_CAPS[]> button_caps( | 164 scoped_ptr<HIDP_BUTTON_CAPS[]> button_caps( |
164 new HIDP_BUTTON_CAPS[button_caps_length]); | 165 new HIDP_BUTTON_CAPS[button_caps_length]); |
165 if (HidP_GetButtonCaps(report_type, | 166 if (HidP_GetButtonCaps(report_type, |
166 &button_caps[0], | 167 &button_caps[0], |
167 &button_caps_length, | 168 &button_caps_length, |
168 preparsed_data) == HIDP_STATUS_SUCCESS) { | 169 preparsed_data) == HIDP_STATUS_SUCCESS) { |
169 for (size_t i = 0; i < button_caps_length; i++) { | 170 for (size_t i = 0; i < button_caps_length; i++) { |
170 int report_id = button_caps[i].ReportID; | 171 int report_id = button_caps[i].ReportID; |
171 if (report_id != 0) { | 172 if (report_id != 0) { |
172 collection_info->report_ids.insert(report_id); | 173 collection_info->report_ids.insert(report_id); |
173 } | 174 } |
174 } | 175 } |
175 } | 176 } |
176 } | 177 } |
177 } | 178 } |
178 | 179 |
180 // static | |
179 void HidServiceWin::CollectInfoFromValueCaps( | 181 void HidServiceWin::CollectInfoFromValueCaps( |
180 PHIDP_PREPARSED_DATA preparsed_data, | 182 PHIDP_PREPARSED_DATA preparsed_data, |
181 HIDP_REPORT_TYPE report_type, | 183 HIDP_REPORT_TYPE report_type, |
182 USHORT value_caps_length, | 184 USHORT value_caps_length, |
183 HidCollectionInfo* collection_info) { | 185 HidCollectionInfo* collection_info) { |
184 if (value_caps_length > 0) { | 186 if (value_caps_length > 0) { |
185 scoped_ptr<HIDP_VALUE_CAPS[]> value_caps( | 187 scoped_ptr<HIDP_VALUE_CAPS[]> value_caps( |
186 new HIDP_VALUE_CAPS[value_caps_length]); | 188 new HIDP_VALUE_CAPS[value_caps_length]); |
187 if (HidP_GetValueCaps( | 189 if (HidP_GetValueCaps( |
188 report_type, &value_caps[0], &value_caps_length, preparsed_data) == | 190 report_type, &value_caps[0], &value_caps_length, preparsed_data) == |
189 HIDP_STATUS_SUCCESS) { | 191 HIDP_STATUS_SUCCESS) { |
190 for (size_t i = 0; i < value_caps_length; i++) { | 192 for (size_t i = 0; i < value_caps_length; i++) { |
191 int report_id = value_caps[i].ReportID; | 193 int report_id = value_caps[i].ReportID; |
192 if (report_id != 0) { | 194 if (report_id != 0) { |
193 collection_info->report_ids.insert(report_id); | 195 collection_info->report_ids.insert(report_id); |
194 } | 196 } |
195 } | 197 } |
196 } | 198 } |
197 } | 199 } |
198 } | 200 } |
199 | 201 |
200 void HidServiceWin::PlatformAddDevice(const std::string& device_path) { | 202 void HidServiceWin::PlatformAddDevice(const std::string& device_path) { |
201 HidDeviceInfo device_info; | 203 HidDeviceInfo device_info; |
202 device_info.device_id = device_path; | 204 device_info.device_id = device_path; |
203 | 205 |
204 // Try to open the device. | 206 // Try to open the device. |
205 base::win::ScopedHandle device_handle( | 207 base::win::ScopedHandle device_handle(OpenDevice(device_path)); |
206 CreateFileA(device_path.c_str(), | 208 if (!device_handle.IsValid()) { |
207 GENERIC_WRITE | GENERIC_READ, | 209 return; |
208 FILE_SHARE_READ | FILE_SHARE_WRITE, | |
209 NULL, | |
210 OPEN_EXISTING, | |
211 FILE_FLAG_OVERLAPPED, | |
212 0)); | |
213 | |
214 if (!device_handle.IsValid() && | |
215 GetLastError() == base::File::FILE_ERROR_ACCESS_DENIED) { | |
216 base::win::ScopedHandle device_handle( | |
217 CreateFileA(device_path.c_str(), | |
218 GENERIC_READ, | |
219 FILE_SHARE_READ, | |
220 NULL, | |
221 OPEN_EXISTING, | |
222 FILE_FLAG_OVERLAPPED, | |
223 0)); | |
224 | |
225 if (!device_handle.IsValid()) | |
226 return; | |
227 } | 210 } |
228 | 211 |
229 // Get VID/PID pair. | 212 // Get VID/PID pair. |
230 HIDD_ATTRIBUTES attrib = {0}; | 213 HIDD_ATTRIBUTES attrib = {0}; |
231 attrib.Size = sizeof(HIDD_ATTRIBUTES); | 214 attrib.Size = sizeof(HIDD_ATTRIBUTES); |
232 if (!HidD_GetAttributes(device_handle.Get(), &attrib)) | 215 if (!HidD_GetAttributes(device_handle.Get(), &attrib)) { |
233 return; | 216 return; |
217 } | |
234 | 218 |
235 device_info.vendor_id = attrib.VendorID; | 219 device_info.vendor_id = attrib.VendorID; |
236 device_info.product_id = attrib.ProductID; | 220 device_info.product_id = attrib.ProductID; |
237 | 221 |
238 for (ULONG i = 32; | |
239 HidD_SetNumInputBuffers(device_handle.Get(), i); | |
240 i <<= 1); | |
241 | |
242 // Get usage and usage page (optional). | 222 // Get usage and usage page (optional). |
243 PHIDP_PREPARSED_DATA preparsed_data; | 223 PHIDP_PREPARSED_DATA preparsed_data; |
244 if (HidD_GetPreparsedData(device_handle.Get(), &preparsed_data) && | 224 if (HidD_GetPreparsedData(device_handle.Get(), &preparsed_data) && |
245 preparsed_data) { | 225 preparsed_data) { |
246 HIDP_CAPS capabilities = {0}; | 226 HIDP_CAPS capabilities = {0}; |
247 if (HidP_GetCaps(preparsed_data, &capabilities) == HIDP_STATUS_SUCCESS) { | 227 if (HidP_GetCaps(preparsed_data, &capabilities) == HIDP_STATUS_SUCCESS) { |
248 device_info.max_input_report_size = capabilities.InputReportByteLength; | 228 device_info.max_input_report_size = capabilities.InputReportByteLength; |
249 device_info.max_output_report_size = capabilities.OutputReportByteLength; | 229 device_info.max_output_report_size = capabilities.OutputReportByteLength; |
250 device_info.max_feature_report_size = | 230 device_info.max_feature_report_size = |
251 capabilities.FeatureReportByteLength; | 231 capabilities.FeatureReportByteLength; |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
297 HidD_FreePreparsedData(preparsed_data); | 277 HidD_FreePreparsedData(preparsed_data); |
298 } | 278 } |
299 | 279 |
300 AddDevice(device_info); | 280 AddDevice(device_info); |
301 } | 281 } |
302 | 282 |
303 void HidServiceWin::PlatformRemoveDevice(const std::string& device_path) { | 283 void HidServiceWin::PlatformRemoveDevice(const std::string& device_path) { |
304 RemoveDevice(device_path); | 284 RemoveDevice(device_path); |
305 } | 285 } |
306 | 286 |
307 void HidServiceWin::GetDevices(std::vector<HidDeviceInfo>* devices) { | 287 base::win::ScopedHandle HidServiceWin::OpenDevice( |
308 Enumerate(); | 288 const std::string& device_path) { |
309 HidService::GetDevices(devices); | 289 base::win::ScopedHandle file( |
310 } | 290 CreateFileA(device_path.c_str(), GENERIC_WRITE | GENERIC_READ, |
311 | 291 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, |
312 void HidServiceWin::Connect(const HidDeviceId& device_id, | 292 FILE_FLAG_OVERLAPPED, NULL)); |
313 const ConnectCallback& callback) { | 293 if (!file.IsValid() && |
314 DCHECK(thread_checker_.CalledOnValidThread()); | 294 GetLastError() == base::File::FILE_ERROR_ACCESS_DENIED) { |
Nico
2015/12/09 17:05:45
Are you sure this is correct? The CreateFile() MSD
Reilly Grant (use Gerrit)
2015/12/09 18:49:29
Interesting. I wonder if this logic has ever been
Nico
2015/12/09 19:10:53
Deleting this block if it's broken and untested so
| |
315 const auto& map_entry = devices().find(device_id); | 295 file.Set(CreateFileA(device_path.c_str(), GENERIC_READ, FILE_SHARE_READ, |
316 if (map_entry == devices().end()) { | 296 NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL)); |
317 task_runner_->PostTask(FROM_HERE, base::Bind(callback, nullptr)); | |
318 return; | |
319 } | 297 } |
320 const HidDeviceInfo& device_info = map_entry->second; | 298 return file.Pass(); |
321 | |
322 scoped_refptr<HidConnectionWin> connection(new HidConnectionWin(device_info)); | |
323 if (!connection->available()) { | |
324 PLOG(ERROR) << "Failed to open device"; | |
325 connection = nullptr; | |
326 } | |
327 task_runner_->PostTask(FROM_HERE, base::Bind(callback, connection)); | |
328 } | 299 } |
329 | 300 |
330 } // namespace device | 301 } // namespace device |
OLD | NEW |