Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / device / hid / hid_service_win.cc
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.
4
5 #include "device/hid/hid_service_win.h"
6
7 #include <cstdlib>
8
9 #include "base/bind.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"
20
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.
32 #pragma comment(lib, "setupapi.lib")
33 #pragma comment(lib, "hid.lib")
34
35 namespace device {
36 namespace {
37
38 const char kHIDClass[] = "HIDClass";
39
40 }  // namespace
41
42 HidServiceWin::HidServiceWin() {
43   base::ThreadRestrictions::AssertIOAllowed();
44   task_runner_ = base::ThreadTaskRunnerHandle::Get();
45   DCHECK(task_runner_.get());
46   Enumerate();
47 }
48
49 HidServiceWin::~HidServiceWin() {}
50
51 void HidServiceWin::Enumerate() {
52   BOOL res;
53   HDEVINFO device_info_set;
54   SP_DEVINFO_DATA devinfo_data;
55   SP_DEVICE_INTERFACE_DATA device_interface_data;
56
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);
60
61   device_info_set = SetupDiGetClassDevs(
62       &GUID_DEVINTERFACE_HID,
63       NULL,
64       NULL,
65       DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
66
67   std::set<std::string> connected_devices;
68
69   if (device_info_set != INVALID_HANDLE_VALUE) {
70     for (int device_index = 0;
71          SetupDiEnumDeviceInterfaces(device_info_set,
72                                      NULL,
73                                      &GUID_DEVINTERFACE_HID,
74                                      device_index,
75                                      &device_interface_data);
76          ++device_index) {
77       DWORD required_size = 0;
78
79       // Determime the required size of detail struct.
80       SetupDiGetDeviceInterfaceDetailA(device_info_set,
81                                        &device_interface_data,
82                                        NULL,
83                                        0,
84                                        &required_size,
85                                        NULL);
86
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);
93
94       // Get the detailed data for this device.
95       res = SetupDiGetDeviceInterfaceDetailA(device_info_set,
96                                              &device_interface_data,
97                                              device_interface_detail_data.get(),
98                                              required_size,
99                                              NULL,
100                                              NULL);
101       if (!res)
102         continue;
103
104       // Enumerate device info. Looking for Setup Class "HIDClass".
105       for (DWORD i = 0;
106           SetupDiEnumDeviceInfo(device_info_set, i, &devinfo_data);
107           i++) {
108         char class_name[256] = {0};
109         res = SetupDiGetDeviceRegistryPropertyA(device_info_set,
110                                                 &devinfo_data,
111                                                 SPDRP_CLASS,
112                                                 NULL,
113                                                 (PBYTE) class_name,
114                                                 sizeof(class_name) - 1,
115                                                 NULL);
116         if (!res)
117           break;
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,
122                                                   &devinfo_data,
123                                                   SPDRP_DRIVER,
124                                                   NULL,
125                                                   (PBYTE) driver_name,
126                                                   sizeof(driver_name) - 1,
127                                                   NULL);
128           if (res) {
129             // Found the driver.
130             break;
131           }
132         }
133       }
134
135       if (!res)
136         continue;
137
138       PlatformAddDevice(device_interface_detail_data->DevicePath);
139       connected_devices.insert(device_interface_detail_data->DevicePath);
140     }
141   }
142
143   // Find disconnected devices.
144   std::vector<std::string> disconnected_devices;
145   for (DeviceMap::const_iterator it = devices().begin(); it != devices().end();
146        ++it) {
147     if (!ContainsKey(connected_devices, it->first)) {
148       disconnected_devices.push_back(it->first);
149     }
150   }
151
152   // Remove disconnected devices.
153   for (size_t i = 0; i < disconnected_devices.size(); ++i) {
154     PlatformRemoveDevice(disconnected_devices[i]);
155   }
156 }
157
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,
167                            &button_caps[0],
168                            &button_caps_length,
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);
174         }
175       }
176     }
177   }
178 }
179
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);
195         }
196       }
197     }
198   }
199 }
200
201 void HidServiceWin::PlatformAddDevice(const std::string& device_path) {
202   HidDeviceInfo device_info;
203   device_info.device_id = device_path;
204
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,
210                   NULL,
211                   OPEN_EXISTING,
212                   FILE_FLAG_OVERLAPPED,
213                   0));
214
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(),
219       GENERIC_READ,
220       FILE_SHARE_READ,
221       NULL,
222       OPEN_EXISTING,
223       FILE_FLAG_OVERLAPPED,
224       0));
225
226     if (!device_handle.IsValid())
227       return;
228   }
229
230   // Get VID/PID pair.
231   HIDD_ATTRIBUTES attrib = {0};
232   attrib.Size = sizeof(HIDD_ATTRIBUTES);
233   if (!HidD_GetAttributes(device_handle.Get(), &attrib))
234     return;
235
236   device_info.vendor_id = attrib.VendorID;
237   device_info.product_id = attrib.ProductID;
238
239   for (ULONG i = 32;
240       HidD_SetNumInputBuffers(device_handle.Get(), i);
241       i <<= 1);
242
243   // Get usage and usage page (optional).
244   PHIDP_PREPARSED_DATA preparsed_data;
245   if (HidD_GetPreparsedData(device_handle.Get(), &preparsed_data) &&
246       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(
255           capabilities.Usage,
256           static_cast<HidUsageAndPage::Page>(capabilities.UsagePage));
257       CollectInfoFromButtonCaps(preparsed_data,
258                                 HidP_Input,
259                                 capabilities.NumberInputButtonCaps,
260                                 &collection_info);
261       CollectInfoFromButtonCaps(preparsed_data,
262                                 HidP_Output,
263                                 capabilities.NumberOutputButtonCaps,
264                                 &collection_info);
265       CollectInfoFromButtonCaps(preparsed_data,
266                                 HidP_Feature,
267                                 capabilities.NumberFeatureButtonCaps,
268                                 &collection_info);
269       CollectInfoFromValueCaps(preparsed_data,
270                                HidP_Input,
271                                capabilities.NumberInputValueCaps,
272                                &collection_info);
273       CollectInfoFromValueCaps(preparsed_data,
274                                HidP_Output,
275                                capabilities.NumberOutputValueCaps,
276                                &collection_info);
277       CollectInfoFromValueCaps(preparsed_data,
278                                HidP_Feature,
279                                capabilities.NumberFeatureValueCaps,
280                                &collection_info);
281       if (!collection_info.report_ids.empty()) {
282         device_info.has_report_id = true;
283       }
284       device_info.collections.push_back(collection_info);
285     }
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--;
291     }
292     if (device_info.max_output_report_size > 0) {
293       device_info.max_output_report_size--;
294     }
295     if (device_info.max_feature_report_size > 0) {
296       device_info.max_feature_report_size--;
297     }
298     HidD_FreePreparsedData(preparsed_data);
299   }
300
301   AddDevice(device_info);
302 }
303
304 void HidServiceWin::PlatformRemoveDevice(const std::string& device_path) {
305   RemoveDevice(device_path);
306 }
307
308 void HidServiceWin::GetDevices(std::vector<HidDeviceInfo>* devices) {
309   Enumerate();
310   HidService::GetDevices(devices);
311 }
312
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));
319     return;
320   }
321   const HidDeviceInfo& device_info = map_entry->second;
322
323   scoped_refptr<HidConnectionWin> connection(new HidConnectionWin(device_info));
324   if (!connection->available()) {
325     PLOG(ERROR) << "Failed to open device";
326     connection = nullptr;
327   }
328   task_runner_->PostTask(FROM_HERE, base::Bind(callback, connection));
329 }
330
331 }  // namespace device