[M71 Dev][Tizen] Fix compiler errors
[platform/framework/web/chromium-efl.git] / device / serial / serial_device_enumerator_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/serial/serial_device_enumerator_win.h"
6
7 #include <windows.h>  // Must be in front of other Windows header files.
8
9 #include <devguid.h>
10 #include <setupapi.h>
11 #include <stdint.h>
12
13 #include <memory>
14 #include <unordered_set>
15
16 #include "base/metrics/histogram_functions.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/string_util.h"
19 #include "base/strings/stringprintf.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "base/threading/scoped_blocking_call.h"
22 #include "base/win/registry.h"
23 #include "third_party/re2/src/re2/re2.h"
24
25 namespace device {
26
27 namespace {
28
29 // Searches the specified device info for a property with the specified key,
30 // assigns the result to value, and returns whether the operation was
31 // successful.
32 bool GetProperty(HDEVINFO dev_info,
33                  SP_DEVINFO_DATA dev_info_data,
34                  const int key,
35                  std::string* value) {
36   // We don't know how much space the property's value will take up, so we call
37   // the property retrieval function once to fetch the size of the required
38   // value buffer, then again once we've allocated a sufficiently large buffer.
39   DWORD buffer_size = 0;
40   SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data, key, nullptr,
41                                    nullptr, buffer_size, &buffer_size);
42   if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
43     return false;
44
45   std::unique_ptr<wchar_t[]> buffer(new wchar_t[buffer_size]);
46   if (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data, key, nullptr,
47                                         reinterpret_cast<PBYTE>(buffer.get()),
48                                         buffer_size, nullptr))
49     return false;
50
51   *value = base::WideToUTF8(buffer.get());
52   return true;
53 }
54
55 // Searches for the COM port in the device's friendly name, assigns its value to
56 // com_port, and returns whether the operation was successful.
57 bool GetCOMPort(const std::string friendly_name, std::string* com_port) {
58   return RE2::PartialMatch(friendly_name, ".* \\((COM[0-9]+)\\)", com_port);
59 }
60
61 // Searches for the display name in the device's friendly name, assigns its
62 // value to display_name, and returns whether the operation was successful.
63 bool GetDisplayName(const std::string friendly_name,
64                     std::string* display_name) {
65   return RE2::PartialMatch(friendly_name, "(.*) \\(COM[0-9]+\\)", display_name);
66 }
67
68 // Searches for the vendor ID in the device's hardware ID, assigns its value to
69 // vendor_id, and returns whether the operation was successful.
70 bool GetVendorID(const std::string hardware_id, uint32_t* vendor_id) {
71   std::string vendor_id_str;
72   return RE2::PartialMatch(hardware_id, "VID_([0-9a-fA-F]+)", &vendor_id_str) &&
73          base::HexStringToUInt(vendor_id_str, vendor_id);
74 }
75
76 // Searches for the product ID in the device's product ID, assigns its value to
77 // product_id, and returns whether the operation was successful.
78 bool GetProductID(const std::string hardware_id, uint32_t* product_id) {
79   std::string product_id_str;
80   return RE2::PartialMatch(hardware_id, "PID_([0-9a-fA-F]+)",
81                            &product_id_str) &&
82          base::HexStringToUInt(product_id_str, product_id);
83 }
84
85 // Returns value clamped to the range of [min, max].
86 int Clamp(int value, int min, int max) {
87   return std::min(std::max(value, min), max);
88 }
89
90 // Returns an array of devices as retrieved through the new method of
91 // enumerating serial devices (SetupDi).  This new method gives more information
92 // about the devices than the old method.
93 std::vector<mojom::SerialDeviceInfoPtr> GetDevicesNew() {
94   std::vector<mojom::SerialDeviceInfoPtr> devices;
95
96   base::ScopedBlockingCall scoped_blocking_call(base::BlockingType::MAY_BLOCK);
97   // Make a device interface query to find all serial devices.
98   HDEVINFO dev_info =
99       SetupDiGetClassDevs(&GUID_DEVCLASS_PORTS, 0, 0, DIGCF_PRESENT);
100   if (dev_info == INVALID_HANDLE_VALUE)
101     return devices;
102
103   SP_DEVINFO_DATA dev_info_data;
104   dev_info_data.cbSize = sizeof(SP_DEVINFO_DATA);
105   for (DWORD i = 0; SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data); i++) {
106     std::string friendly_name, com_port;
107     // SPDRP_FRIENDLYNAME looks like "USB_SERIAL_PORT (COM3)".
108     if (!GetProperty(dev_info, dev_info_data, SPDRP_FRIENDLYNAME,
109                      &friendly_name) ||
110         !GetCOMPort(friendly_name, &com_port))
111       // In Windows, the COM port is the path used to uniquely identify the
112       // serial device. If the COM can't be found, ignore the device.
113       continue;
114
115     auto info = mojom::SerialDeviceInfo::New();
116     info->path = com_port;
117
118     std::string display_name;
119     if (GetDisplayName(friendly_name, &display_name))
120       info->display_name = std::move(display_name);
121
122     std::string hardware_id;
123     // SPDRP_HARDWAREID looks like "FTDIBUS\COMPORT&VID_0403&PID_6001".
124     if (GetProperty(dev_info, dev_info_data, SPDRP_HARDWAREID, &hardware_id)) {
125       uint32_t vendor_id, product_id;
126       if (GetVendorID(hardware_id, &vendor_id)) {
127         info->has_vendor_id = true;
128         info->vendor_id = vendor_id;
129       }
130       if (GetProductID(hardware_id, &product_id)) {
131         info->has_product_id = true;
132         info->product_id = product_id;
133       }
134     }
135
136     devices.push_back(std::move(info));
137   }
138
139   SetupDiDestroyDeviceInfoList(dev_info);
140   return devices;
141 }
142
143 // Returns an array of devices as retrieved through the old method of
144 // enumerating serial devices (searching the registry). This old method gives
145 // less information about the devices than the new method.
146 std::vector<mojom::SerialDeviceInfoPtr> GetDevicesOld() {
147   base::ScopedBlockingCall scoped_blocking_call(base::BlockingType::MAY_BLOCK);
148   base::win::RegistryValueIterator iter_key(
149       HKEY_LOCAL_MACHINE, L"HARDWARE\\DEVICEMAP\\SERIALCOMM\\");
150   std::vector<mojom::SerialDeviceInfoPtr> devices;
151   for (; iter_key.Valid(); ++iter_key) {
152     auto info = mojom::SerialDeviceInfo::New();
153     info->path = base::UTF16ToASCII(iter_key.Value());
154     devices.push_back(std::move(info));
155   }
156   return devices;
157 }
158
159 }  // namespace
160
161 // static
162 std::unique_ptr<SerialDeviceEnumerator> SerialDeviceEnumerator::Create() {
163   return std::unique_ptr<SerialDeviceEnumerator>(
164       new SerialDeviceEnumeratorWin());
165 }
166
167 SerialDeviceEnumeratorWin::SerialDeviceEnumeratorWin() {}
168
169 SerialDeviceEnumeratorWin::~SerialDeviceEnumeratorWin() {}
170
171 std::vector<mojom::SerialDeviceInfoPtr>
172 SerialDeviceEnumeratorWin::GetDevices() {
173   std::vector<mojom::SerialDeviceInfoPtr> devices = GetDevicesNew();
174   std::vector<mojom::SerialDeviceInfoPtr> old_devices = GetDevicesOld();
175
176   base::UmaHistogramSparse("Hardware.Serial.NewMinusOldDeviceListSize",
177                            Clamp(devices.size() - old_devices.size(), -10, 10));
178
179   // Add devices found from both the new and old methods of enumeration. If a
180   // device is found using both the new and the old enumeration method, then we
181   // take the device from the new enumeration method because it's able to
182   // collect more information. We do this by inserting the new devices first,
183   // because insertions are ignored if the key already exists.
184   std::unordered_set<std::string> devices_seen;
185   for (const auto& device : devices) {
186     bool inserted = devices_seen.insert(device->path).second;
187     DCHECK(inserted);
188   }
189   for (auto& device : old_devices) {
190     if (devices_seen.insert(device->path).second)
191       devices.push_back(std::move(device));
192   }
193   return devices;
194 }
195
196 }  // namespace device