Upload upstream chromium 108.0.5359.1
[platform/framework/web/chromium-efl.git] / services / device / serial / serial_device_enumerator_linux.cc
1 // Copyright 2014 The Chromium Authors
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_linux.h"
6
7 #include <stdint.h>
8
9 #include <memory>
10 #include <utility>
11 #include <vector>
12
13 #include "base/check_op.h"
14 #include "base/files/file_util.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_split.h"
17 #include "base/strings/string_util.h"
18 #include "base/threading/scoped_blocking_call.h"
19 #include "components/device_event_log/device_event_log.h"
20 #include "device/udev_linux/udev.h"
21
22 namespace device {
23
24 namespace {
25
26 // Holds information about a TTY driver for serial devices. Each driver creates
27 // device nodes with a given major number and in a range of minor numbers.
28 struct SerialDriverInfo {
29   int major;
30   int minor_start;
31   int minor_end;  // Inclusive.
32 };
33
34 std::vector<SerialDriverInfo> ReadSerialDriverInfo(const base::FilePath& path) {
35   std::string tty_drivers;
36   if (!base::ReadFileToString(path, &tty_drivers)) {
37     return {};
38   }
39
40   // Each line has information on a single TTY driver.
41   std::vector<SerialDriverInfo> serial_drivers;
42   for (const auto& line :
43        base::SplitStringPiece(tty_drivers, "\n", base::KEEP_WHITESPACE,
44                               base::SPLIT_WANT_NONEMPTY)) {
45     std::vector<base::StringPiece> fields = base::SplitStringPiece(
46         line, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
47
48     // The format of each line is:
49     //
50     //   driver name<SPACE>name<SPACE>major<SPACE>minor range<SPACE>type
51     //
52     // We only care about drivers that provide the "serial" type. The rest are
53     // things like pseudoterminals.
54     if (fields.size() < 5 || fields[4] != "serial")
55       continue;
56
57     SerialDriverInfo info;
58     if (!base::StringToInt(fields[2], &info.major))
59       continue;
60
61     std::vector<base::StringPiece> minor_range = base::SplitStringPiece(
62         fields[3], "-", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
63     if (minor_range.size() == 1) {
64       if (!base::StringToInt(minor_range[0], &info.minor_start))
65         continue;
66       info.minor_end = info.minor_start;
67     } else if (minor_range.size() == 2) {
68       if (!base::StringToInt(minor_range[0], &info.minor_start) ||
69           !base::StringToInt(minor_range[1], &info.minor_end)) {
70         continue;
71       }
72     } else {
73       continue;
74     }
75
76     serial_drivers.push_back(info);
77   }
78
79   return serial_drivers;
80 }
81
82 }  // namespace
83
84 // static
85 std::unique_ptr<SerialDeviceEnumeratorLinux>
86 SerialDeviceEnumeratorLinux::Create() {
87   return std::make_unique<SerialDeviceEnumeratorLinux>(
88       base::FilePath("/proc/tty/drivers"));
89 }
90
91 SerialDeviceEnumeratorLinux::SerialDeviceEnumeratorLinux(
92     const base::FilePath& tty_driver_info_path)
93     : tty_driver_info_path_(tty_driver_info_path) {
94   DETACH_FROM_SEQUENCE(sequence_checker_);
95
96   watcher_ = UdevWatcher::StartWatching(this);
97   if (watcher_)
98     watcher_->EnumerateExistingDevices();
99 }
100
101 SerialDeviceEnumeratorLinux::~SerialDeviceEnumeratorLinux() {
102   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
103 }
104
105 void SerialDeviceEnumeratorLinux::OnDeviceAdded(ScopedUdevDevicePtr device) {
106   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
107   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
108                                                 base::BlockingType::MAY_BLOCK);
109
110   const char* subsystem = udev_device_get_subsystem(device.get());
111   if (!subsystem || strcmp(subsystem, "tty") != 0)
112     return;
113
114   const char* syspath_str = udev_device_get_syspath(device.get());
115   if (!syspath_str)
116     return;
117   std::string syspath(syspath_str);
118
119   const char* major_str = udev_device_get_property_value(device.get(), "MAJOR");
120   const char* minor_str = udev_device_get_property_value(device.get(), "MINOR");
121
122   int major, minor;
123   if (!major_str || !minor_str || !base::StringToInt(major_str, &major) ||
124       !base::StringToInt(minor_str, &minor)) {
125     return;
126   }
127
128   for (const auto& driver : ReadSerialDriverInfo(tty_driver_info_path_)) {
129     if (major == driver.major && minor >= driver.minor_start &&
130         minor <= driver.minor_end) {
131       CreatePort(std::move(device), syspath);
132       return;
133     }
134   }
135 }
136
137 void SerialDeviceEnumeratorLinux::OnDeviceChanged(ScopedUdevDevicePtr device) {}
138
139 void SerialDeviceEnumeratorLinux::OnDeviceRemoved(ScopedUdevDevicePtr device) {
140   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
141   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
142                                                 base::BlockingType::MAY_BLOCK);
143
144   const char* syspath = udev_device_get_syspath(device.get());
145   if (!syspath)
146     return;
147
148   auto it = paths_.find(syspath);
149   if (it == paths_.end())
150     return;
151   base::UnguessableToken token = it->second;
152
153   paths_.erase(it);
154   RemovePort(token);
155 }
156
157 void SerialDeviceEnumeratorLinux::CreatePort(ScopedUdevDevicePtr device,
158                                              const std::string& syspath) {
159   const char* path = udev_device_get_property_value(device.get(), "DEVNAME");
160   if (!path)
161     return;
162
163   auto token = base::UnguessableToken::Create();
164   auto info = mojom::SerialPortInfo::New();
165   info->path = base::FilePath(path);
166   info->token = token;
167
168   uint32_t int_value;
169   const char* vendor_id =
170       udev_device_get_property_value(device.get(), "ID_VENDOR_ID");
171   if (vendor_id && base::HexStringToUInt(vendor_id, &int_value)) {
172     info->vendor_id = int_value;
173     info->has_vendor_id = true;
174   }
175
176   const char* product_id =
177       udev_device_get_property_value(device.get(), "ID_MODEL_ID");
178   if (product_id && base::HexStringToUInt(product_id, &int_value)) {
179     info->product_id = int_value;
180     info->has_product_id = true;
181   }
182
183   const char* product_name_enc =
184       udev_device_get_property_value(device.get(), "ID_MODEL_ENC");
185   if (product_name_enc)
186     info->display_name = device::UdevDecodeString(product_name_enc);
187
188   const char* serial_number =
189       udev_device_get_property_value(device.get(), "ID_SERIAL_SHORT");
190   if (serial_number)
191     info->serial_number = serial_number;
192
193   SERIAL_LOG(EVENT) << "Serial device added: path=" << info->path
194                     << " vid=" << (vendor_id ? vendor_id : "(none)")
195                     << " pid=" << (product_id ? product_id : "(none)")
196                     << " serial=" << info->serial_number.value_or("(none)");
197
198   paths_.insert(std::make_pair(syspath, token));
199   AddPort(std::move(info));
200 }
201
202 }  // namespace device