blob: 7243bdc9aaac5a4490b4b96a0f255c842f76fa36 [file]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "services/device/serial/serial_device_enumerator_linux.h"
#include <stdint.h>
#include <memory>
#include <utility>
#include <vector>
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/threading/scoped_blocking_call.h"
namespace device {
namespace {
const char kSerialSubsystem[] = "tty";
const char kHostPathKey[] = "DEVNAME";
const char kHostBusKey[] = "ID_BUS";
const char kMajorKey[] = "MAJOR";
const char kVendorIDKey[] = "ID_VENDOR_ID";
const char kProductIDKey[] = "ID_MODEL_ID";
const char kProductNameKey[] = "ID_MODEL";
// The major number for an RFCOMM TTY device.
const char kRfcommMajor[] = "216";
} // namespace
// static
std::unique_ptr<SerialDeviceEnumerator> SerialDeviceEnumerator::Create() {
return std::make_unique<SerialDeviceEnumeratorLinux>();
}
SerialDeviceEnumeratorLinux::SerialDeviceEnumeratorLinux() {
DETACH_FROM_SEQUENCE(sequence_checker_);
watcher_ = UdevWatcher::StartWatching(this);
watcher_->EnumerateExistingDevices();
}
SerialDeviceEnumeratorLinux::~SerialDeviceEnumeratorLinux() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
std::vector<mojom::SerialPortInfoPtr>
SerialDeviceEnumeratorLinux::GetDevices() {
std::vector<mojom::SerialPortInfoPtr> ports;
ports.reserve(ports_.size());
for (const auto& map_entry : ports_)
ports.push_back(map_entry.second->Clone());
return ports;
}
base::Optional<base::FilePath> SerialDeviceEnumeratorLinux::GetPathFromToken(
const base::UnguessableToken& token) {
auto it = ports_.find(token);
if (it == ports_.end())
return base::nullopt;
return it->second->path;
}
void SerialDeviceEnumeratorLinux::OnDeviceAdded(ScopedUdevDevicePtr device) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
const char* subsystem = udev_device_get_subsystem(device.get());
if (!subsystem || strcmp(subsystem, kSerialSubsystem) != 0)
return;
const char* syspath_str = udev_device_get_syspath(device.get());
if (!syspath_str)
return;
std::string syspath(syspath_str);
// Platform serial ports.
if (base::StartsWith(syspath, "/sys/devices/platform/",
base::CompareCase::SENSITIVE)) {
CreatePort(std::move(device), syspath);
return;
}
// USB serial ports and others that have a proper bus identifier.
const char* bus = udev_device_get_property_value(device.get(), kHostBusKey);
if (bus) {
CreatePort(std::move(device), syspath);
return;
}
// Bluetooth ports are virtual TTYs but have an identifiable major number.
const char* major = udev_device_get_property_value(device.get(), kMajorKey);
if (major && base::StringPiece(major) == kRfcommMajor) {
CreatePort(std::move(device), syspath);
return;
}
}
void SerialDeviceEnumeratorLinux::OnDeviceChanged(ScopedUdevDevicePtr device) {}
void SerialDeviceEnumeratorLinux::OnDeviceRemoved(ScopedUdevDevicePtr device) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
const char* syspath = udev_device_get_syspath(device.get());
if (!syspath)
return;
auto it = paths_.find(syspath);
if (it == paths_.end())
return;
ports_.erase(it->second);
paths_.erase(it);
}
void SerialDeviceEnumeratorLinux::CreatePort(ScopedUdevDevicePtr device,
const std::string& syspath) {
const char* path = udev_device_get_property_value(device.get(), kHostPathKey);
if (!path)
return;
auto token = base::UnguessableToken::Create();
auto info = mojom::SerialPortInfo::New();
info->path = base::FilePath(path);
info->token = token;
const char* vendor_id =
udev_device_get_property_value(device.get(), kVendorIDKey);
const char* product_id =
udev_device_get_property_value(device.get(), kProductIDKey);
const char* product_name =
udev_device_get_property_value(device.get(), kProductNameKey);
uint32_t int_value;
if (vendor_id && base::HexStringToUInt(vendor_id, &int_value)) {
info->vendor_id = int_value;
info->has_vendor_id = true;
}
if (product_id && base::HexStringToUInt(product_id, &int_value)) {
info->product_id = int_value;
info->has_product_id = true;
}
if (product_name)
info->display_name.emplace(product_name);
ports_.insert(std::make_pair(token, std::move(info)));
paths_.insert(std::make_pair(syspath, token));
}
} // namespace device