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.
5 #include "services/device/serial/serial_device_enumerator_linux.h"
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"
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 {
31 int minor_end; // Inclusive.
34 std::vector<SerialDriverInfo> ReadSerialDriverInfo(const base::FilePath& path) {
35 std::string tty_drivers;
36 if (!base::ReadFileToString(path, &tty_drivers)) {
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);
48 // The format of each line is:
50 // driver name<SPACE>name<SPACE>major<SPACE>minor range<SPACE>type
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")
57 SerialDriverInfo info;
58 if (!base::StringToInt(fields[2], &info.major))
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))
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)) {
76 serial_drivers.push_back(info);
79 return serial_drivers;
85 std::unique_ptr<SerialDeviceEnumeratorLinux>
86 SerialDeviceEnumeratorLinux::Create() {
87 return std::make_unique<SerialDeviceEnumeratorLinux>(
88 base::FilePath("/proc/tty/drivers"));
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_);
96 watcher_ = UdevWatcher::StartWatching(this);
98 watcher_->EnumerateExistingDevices();
101 SerialDeviceEnumeratorLinux::~SerialDeviceEnumeratorLinux() {
102 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
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);
110 const char* subsystem = udev_device_get_subsystem(device.get());
111 if (!subsystem || strcmp(subsystem, "tty") != 0)
114 const char* syspath_str = udev_device_get_syspath(device.get());
117 std::string syspath(syspath_str);
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");
123 if (!major_str || !minor_str || !base::StringToInt(major_str, &major) ||
124 !base::StringToInt(minor_str, &minor)) {
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);
137 void SerialDeviceEnumeratorLinux::OnDeviceChanged(ScopedUdevDevicePtr device) {}
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);
144 const char* syspath = udev_device_get_syspath(device.get());
148 auto it = paths_.find(syspath);
149 if (it == paths_.end())
151 base::UnguessableToken token = it->second;
157 void SerialDeviceEnumeratorLinux::CreatePort(ScopedUdevDevicePtr device,
158 const std::string& syspath) {
159 const char* path = udev_device_get_property_value(device.get(), "DEVNAME");
163 auto token = base::UnguessableToken::Create();
164 auto info = mojom::SerialPortInfo::New();
165 info->path = base::FilePath(path);
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;
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;
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);
188 const char* serial_number =
189 udev_device_get_property_value(device.get(), "ID_SERIAL_SHORT");
191 info->serial_number = serial_number;
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)");
198 paths_.insert(std::make_pair(syspath, token));
199 AddPort(std::move(info));
202 } // namespace device