typecd: Add Cable speed metric
Report the cables supported USB speed based on the value reported in
the cable VDO.
BUG=b:185157607
TEST=Unit tests still pass. typec.Mode* tests pass.
Change-Id: I8af59c551f0a4c55f20844b0613c93aa18fbe5a5
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/2822891
Tested-by: Prashant Malani <pmalani@chromium.org>
Commit-Queue: Prashant Malani <pmalani@chromium.org>
Reviewed-by: Mengqi Guo <mqg@chromium.org>
diff --git a/typecd/cable.cc b/typecd/cable.cc
index 3a56cf7..0231dbb 100644
--- a/typecd/cable.cc
+++ b/typecd/cable.cc
@@ -156,10 +156,76 @@
return num_alt_modes_ == alt_modes_.size();
}
+CableSpeedMetric Cable::GetCableSpeedMetric() {
+ CableSpeedMetric ret = CableSpeedMetric::kOther;
+
+ // If we can't identify a valid cable in the ID Header, return early.
+ auto cable_type = (GetIdHeaderVDO() >> kIDHeaderVDOProductTypeBitOffset) &
+ kIDHeaderVDOProductTypeMask;
+ if (!(cable_type == kIDHeaderVDOProductTypeCableActive ||
+ cable_type == kIDHeaderVDOProductTypeCablePassive))
+ return ret;
+
+ // Parse the speed field in the Cable VDO.
+ auto cable_vdo = GetProductTypeVDO1();
+ uint32_t speed = cable_vdo & kUSBSpeedBitMask;
+ switch (speed) {
+ case kUSBSpeed20:
+ ret = CableSpeedMetric::kUSB2_0;
+ break;
+ case kUSBSuperSpeed32Gen1:
+ ret = CableSpeedMetric::kUSB3_2Gen1;
+ break;
+ case kUSBSuperSpeed32Or40Gen2:
+ ret = CableSpeedMetric::kUSB3_2USB4Gen2;
+ break;
+ case kUSB40SuperSpeedGen3:
+ ret = CableSpeedMetric::kUSB4Gen3;
+ break;
+ }
+
+ // Add special handling for the PD 2.0 Cable VDO speed.
+ if (GetPDRevision() == PDRevision::k20) {
+ if (speed == kUSBSuperSpeed31Gen1) {
+ ret = CableSpeedMetric::kUSB3_1Gen1;
+ } else if (speed == kUSBSuperSpeed31Gen2) {
+ ret = CableSpeedMetric::kUSB3_1Gen1Gen2;
+ }
+ }
+
+ if (ret != CableSpeedMetric::kUSB2_0)
+ return ret;
+
+ // Finally, handle TBT-only cables (only if the VDOs claim to only
+ // support USB 2.0 speeds).
+ for (const auto& [index, mode] : alt_modes_) {
+ if (mode->GetSVID() != kTBTAltModeVID)
+ continue;
+
+ uint32_t tbt_vdo = mode->GetVDO();
+
+ // If rounded support is there, we should continue.
+ auto rounded_support =
+ (tbt_vdo >> kTBT3CableDiscModeVDORoundedSupportOffset) &
+ kTBT3CableDiscModeVDORoundedSupportMask;
+ if (rounded_support == kTBT3CableDiscModeVDO_3_4_Gen_Rounded_Non_Rounded)
+ continue;
+
+ auto speed = (tbt_vdo >> kTBT3CableDiscModeVDOSpeedOffset) &
+ kTBT3CableDiscModeVDOSpeedMask;
+ if (speed == kTBT3CableDiscModeVDOSpeed10G20G)
+ ret = CableSpeedMetric::kTBTOnly10G20G;
+ }
+
+ return ret;
+}
+
void Cable::ReportMetrics(Metrics* metrics) {
if (!metrics || metrics_reported_)
return;
+ metrics->ReportCableSpeed(GetCableSpeedMetric());
+
metrics_reported_ = true;
}
diff --git a/typecd/cable.h b/typecd/cable.h
index e584212..683dde4 100644
--- a/typecd/cable.h
+++ b/typecd/cable.h
@@ -85,6 +85,13 @@
void ReportMetrics(Metrics* metrics);
private:
+ friend class MetricsTest;
+ FRIEND_TEST(MetricsTest, CheckCableSpeedTBTOnly);
+ FRIEND_TEST(MetricsTest, CheckCableSpeedPassive40Gbps);
+ FRIEND_TEST(MetricsTest, CheckCableSpeedPassiveUSB31_Gen1);
+
+ CableSpeedMetric GetCableSpeedMetric();
+
// Map representing all SOP' alternate modes.
// The key is the index of the alternate mode as determined
// by the connector class sysfs directory. For example,
diff --git a/typecd/metrics.cc b/typecd/metrics.cc
index edd83b4..b06ad3f 100644
--- a/typecd/metrics.cc
+++ b/typecd/metrics.cc
@@ -6,6 +6,7 @@
namespace {
constexpr char kPartnerTypeMetricName[] = "ChromeOS.TypeC.PartnerType";
+constexpr char kCableSpeedMetricName[] = "ChromeOS.TypeC.CableSpeed";
} // namespace
namespace typecd {
@@ -19,4 +20,13 @@
}
}
+void Metrics::ReportCableSpeed(CableSpeedMetric speed) {
+ if (!metrics_library_.SendEnumToUMA(
+ kCableSpeedMetricName, static_cast<int>(speed),
+ static_cast<int>(CableSpeedMetric::kMaxValue) + 1)) {
+ LOG(WARNING) << "Failed to send cable speed sample to UMA, speed: "
+ << static_cast<int>(speed);
+ }
+}
+
} // namespace typecd
diff --git a/typecd/metrics.h b/typecd/metrics.h
index f3c97bf..031eac8 100644
--- a/typecd/metrics.h
+++ b/typecd/metrics.h
@@ -26,6 +26,20 @@
kMaxValue = kUSBPeripheral,
};
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class CableSpeedMetric {
+ kOther = 0,
+ kUSB2_0 = 1,
+ kUSB3_2Gen1 = 2,
+ kUSB3_2USB4Gen2 = 3,
+ kUSB3_1Gen1 = 4,
+ kUSB3_1Gen1Gen2 = 5,
+ kUSB4Gen3 = 6,
+ kTBTOnly10G20G = 7,
+ kMaxValue = kTBTOnly10G20G,
+};
+
// A class for collecting UMA metrics.
class Metrics {
public:
@@ -36,6 +50,7 @@
~Metrics() = default;
void ReportPartnerType(PartnerTypeMetric type);
+ void ReportCableSpeed(CableSpeedMetric speed);
private:
MetricsLibrary metrics_library_;
diff --git a/typecd/metrics_test.cc b/typecd/metrics_test.cc
index d97de9a..87323cf 100644
--- a/typecd/metrics_test.cc
+++ b/typecd/metrics_test.cc
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "typecd/cable.h"
#include "typecd/partner.h"
#include <base/files/scoped_temp_dir.h>
@@ -182,4 +183,65 @@
EXPECT_EQ(PartnerTypeMetric::kOther, p.GetPartnerTypeMetric());
}
+TEST_F(MetricsTest, CheckCableSpeedTBTOnly) {
+ // Belkin TBT3 Active Cable 40Gbps.
+ Cable c(base::FilePath("foo"));
+
+ c.SetPDRevision(PDRevision::k20);
+ c.SetIdHeaderVDO(0x240020c2);
+ c.SetCertStatVDO(0x0);
+ c.SetProductVDO(0x40010);
+ c.SetProductTypeVDO1(0x21085858);
+ c.SetProductTypeVDO2(0x0);
+ c.SetProductTypeVDO3(0x0);
+
+ c.SetNumAltModes(2);
+
+ std::string mode_dirname = base::StringPrintf("port%d-plug0.%d", 0, 0);
+ auto mode_path = temp_dir_.Append(mode_dirname);
+ ASSERT_TRUE(CreateFakeAltMode(mode_path, kTBTAltModeVID, 0x430001, 0));
+ c.AddAltMode(mode_path);
+
+ mode_dirname = base::StringPrintf("port%d-plug0.%d", 0, 1);
+ mode_path = temp_dir_.Append(mode_dirname);
+ ASSERT_TRUE(CreateFakeAltMode(mode_path, 0x04b4, 0x1, 0));
+ c.AddAltMode(mode_path);
+
+ EXPECT_EQ(CableSpeedMetric::kTBTOnly10G20G, c.GetCableSpeedMetric());
+}
+
+TEST_F(MetricsTest, CheckCableSpeedPassive40Gbps) {
+ // StarTech Passive Cable 40 Gbps PD 2.0
+ Cable c(base::FilePath("foo"));
+
+ c.SetPDRevision(PDRevision::k20);
+ c.SetIdHeaderVDO(0x1c0020c2);
+ c.SetCertStatVDO(0x000000b6);
+ c.SetProductVDO(0x00010310);
+ c.SetProductTypeVDO1(0x11082052);
+ c.SetProductTypeVDO2(0x0);
+ c.SetProductTypeVDO3(0x0);
+
+ c.SetNumAltModes(0);
+
+ EXPECT_EQ(CableSpeedMetric::kUSB3_1Gen1Gen2, c.GetCableSpeedMetric());
+}
+
+TEST_F(MetricsTest, CheckCableSpeedPassiveUSB31_Gen1) {
+ // Hongju Full USB 3.1 Gen 1 5A passive cable.
+ Cable c(base::FilePath("foo"));
+
+ c.SetPDRevision(PDRevision::k20);
+ c.SetIdHeaderVDO(0x18005694);
+ c.SetCertStatVDO(0x88);
+ c.SetProductVDO(0xce901a0);
+ c.SetProductTypeVDO1(0x84051);
+ c.SetProductTypeVDO2(0x0);
+ c.SetProductTypeVDO3(0x0);
+
+ c.SetNumAltModes(0);
+
+ EXPECT_EQ(CableSpeedMetric::kUSB3_1Gen1, c.GetCableSpeedMetric());
+}
+
} // namespace typecd
diff --git a/typecd/pd_vdo_constants.h b/typecd/pd_vdo_constants.h
index f1c19b9..0ea9814 100644
--- a/typecd/pd_vdo_constants.h
+++ b/typecd/pd_vdo_constants.h
@@ -62,6 +62,9 @@
constexpr uint8_t kTBT3CableDiscModeVDORoundedSupportOffset = 19;
constexpr uint8_t kTBT3CableDiscModeVDORoundedSupportMask = 0x3;
constexpr uint8_t kTBT3CableDiscModeVDO_3_4_Gen_Rounded_Non_Rounded = 0x1;
+constexpr uint8_t kTBT3CableDiscModeVDOSpeedOffset = 16;
+constexpr uint8_t kTBT3CableDiscModeVDOSpeedMask = 0x7;
+constexpr uint8_t kTBT3CableDiscModeVDOSpeed10G20G = 0x3;
// Standard and Vendor Indentifications commonly expected in cables and partners
constexpr uint16_t kDPAltModeSID = 0xff01;