blob: 7dcacce0b81670de55e2e6819af4998a256efbb5 [file] [log] [blame]
// Copyright 2022 The Chromium OS 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 "typecd/usb_monitor.h"
#include <utility>
#include <base/files/file_util.h>
#include <base/logging.h>
#include "base/strings/string_number_conversions.h"
#include <base/strings/string_util.h>
#include <re2/re2.h>
namespace {
constexpr char kInterfaceFilePathRegex[] =
R"((\d+)-(\d+)(\.(\d+))*:(\d+)\.(\d+))";
constexpr char kTypecPortUeventRegex[] = R"(TYPEC_PORT=port(\d+))";
typecd::UsbSpeed ConvertToUsbSpeed(std::string speed) {
if (speed == "1.5")
return typecd::UsbSpeed::k1_5;
else if (speed == "12")
return typecd::UsbSpeed::k12;
else if (speed == "480")
return typecd::UsbSpeed::k480;
else if (speed == "5000")
return typecd::UsbSpeed::k5000;
else if (speed == "10000")
return typecd::UsbSpeed::k10000;
else if (speed == "20000")
return typecd::UsbSpeed::k20000;
else
return typecd::UsbSpeed::kOther;
}
typecd::UsbDeviceClass ConvertToUsbClass(std::string device_class) {
if (device_class == "00")
return typecd::UsbDeviceClass::kNone;
else if (device_class == "09")
return typecd::UsbDeviceClass::kHub;
else
return typecd::UsbDeviceClass::kOther;
}
// Convert version string parsed from USB device sysfs to UsbVersion enum to
// store in UsbDevice.
typecd::UsbVersion ConvertToUsbVersion(std::string version) {
if (version == "1.00")
return typecd::UsbVersion::k1_0;
else if (version == "1.10")
return typecd::UsbVersion::k1_1;
else if (version == "2.00")
return typecd::UsbVersion::k2_0;
else if (version == "2.10")
return typecd::UsbVersion::k2_1;
else if (version == "3.00")
return typecd::UsbVersion::k3_0;
else if (version == "3.10")
return typecd::UsbVersion::k3_1;
else if (version == "3.20")
return typecd::UsbVersion::k3_2;
else
return typecd::UsbVersion::kOther;
}
} // namespace
namespace typecd {
UsbMonitor::UsbMonitor() : metrics_(nullptr) {}
void UsbMonitor::OnDeviceAddedOrRemoved(const base::FilePath& path,
bool added) {
auto key = path.BaseName().value();
if (RE2::FullMatch(key, kInterfaceFilePathRegex)) {
return;
}
auto it = devices_.find(key);
if (added) {
if (it != devices_.end()) {
LOG(WARNING) << "Attempting to add an already added usb device in "
<< path;
return;
}
std::string busnum;
std::string devnum;
if (!base::ReadFileToString(path.Append("busnum"), &busnum)) {
PLOG(ERROR) << "Failed to find busnum in " << path;
return;
}
if (!base::ReadFileToString(path.Append("devnum"), &devnum)) {
PLOG(ERROR) << "Failed to find devnum in " << path;
return;
}
base::TrimWhitespaceASCII(busnum, base::TRIM_TRAILING, &busnum);
base::TrimWhitespaceASCII(devnum, base::TRIM_TRAILING, &devnum);
int busnum_int;
int devnum_int;
if (!base::StringToInt(busnum, &busnum_int) ||
!base::StringToInt(devnum, &devnum_int)) {
PLOG(ERROR) << "Failed to parse integer value from busnum and devnum in "
<< path;
return;
}
devices_.emplace(key,
std::make_unique<UsbDevice>(busnum_int, devnum_int, key));
std::string typec_port_uevent;
int typec_port_num;
if (base::ReadFileToString(path.Append("port/connector/uevent"),
&typec_port_uevent) &&
RE2::PartialMatch(typec_port_uevent, kTypecPortUeventRegex,
&typec_port_num)) {
GetDevice(key)->SetTypecPortNum(typec_port_num);
} else {
// Parent USB hub device's sysfs directory name.
// e.g. Parent sysfs of 2-1.5.4 would be 2-1
auto parent_key = key.substr(0, key.find("."));
// If parent USB hub device is present and has a Type C port associated,
// use the parent's Type C port number.
if (GetDevice(parent_key) != nullptr)
GetDevice(key)->SetTypecPortNum(
GetDevice(parent_key)->GetTypecPortNum());
}
std::string speed;
if (base::ReadFileToString(path.Append("speed"), &speed)) {
base::TrimWhitespaceASCII(speed, base::TRIM_TRAILING, &speed);
GetDevice(key)->SetSpeed(ConvertToUsbSpeed(speed));
}
std::string device_class;
if (base::ReadFileToString(path.Append("bDeviceClass"), &device_class)) {
base::TrimWhitespaceASCII(device_class, base::TRIM_TRAILING,
&device_class);
GetDevice(key)->SetDeviceClass(ConvertToUsbClass(device_class));
}
std::string version;
if (base::ReadFileToString(path.Append("version"), &version)) {
base::TrimWhitespaceASCII(version, base::TRIM_ALL, &version);
GetDevice(key)->SetVersion(ConvertToUsbVersion(version));
}
ReportMetrics(path, key);
} else {
if (it == devices_.end()) {
LOG(WARNING) << "Attempting to remove a non-existent usb device in "
<< path;
return;
}
devices_.erase(key);
}
}
UsbDevice* UsbMonitor::GetDevice(std::string key) {
auto it = devices_.find(key);
if (it == devices_.end())
return nullptr;
return it->second.get();
}
void UsbMonitor::ReportMetrics(const base::FilePath& path, std::string key) {
if (!metrics_)
return;
UsbDevice* device = GetDevice(key);
if (!device) {
LOG(WARNING)
<< "Metrics reporting attempted for non-existent usb device in "
<< path;
return;
}
device->ReportMetrics(metrics_);
}
} // namespace typecd