Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(460)

Side by Side Diff: device/hid/hid_service_win.cc

Issue 783773002: Register for HID device notifications on Windows. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « device/hid/hid_service_win.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
OLDNEW
« no previous file with comments | « device/hid/hid_service_win.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698