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