blob: fe42d872729771fbe0e1b14199b9ad6a4afc1c83 [file] [log] [blame]
// Copyright 2018 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 "shill/device_id.h"
#include <inttypes.h>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
namespace shill {
namespace {
// Reads a file containing a string device ID and normalizes it by trimming
// whitespace and converting to lowercase.
bool ReadDeviceIdFile(const base::FilePath& path, std::string* out_id) {
DCHECK(out_id);
std::string contents;
if (!base::ReadFileToString(path, &contents))
return false;
*out_id = base::CollapseWhitespaceASCII(base::ToLowerASCII(contents), true);
return true;
}
bool HextetToUInt16(const std::string& input, uint16_t* output) {
DCHECK(output);
std::vector<uint8_t> bytes;
if (!base::HexStringToBytes(input, &bytes))
return false;
if (bytes.size() != 2)
return false;
*output = bytes[0] << 8 | bytes[1];
return true;
}
bool HexToUInt16(const std::string& input, uint16_t* output) {
DCHECK(output);
if (base::StartsWith(input, "0x", base::CompareCase::INSENSITIVE_ASCII)) {
return HextetToUInt16(input.substr(2), output);
}
return HextetToUInt16(input, output);
}
std::unique_ptr<DeviceId> ReadDeviceId(DeviceId::BusType bus_type,
const base::FilePath& vendor_path,
const base::FilePath& product_path) {
std::string vendor_id, product_id;
uint16_t parsed_vendor_id, parsed_product_id;
if (!ReadDeviceIdFile(vendor_path, &vendor_id) ||
!HexToUInt16(vendor_id, &parsed_vendor_id)) {
return std::make_unique<DeviceId>(bus_type);
}
if (!ReadDeviceIdFile(product_path, &product_id) ||
!HexToUInt16(product_id, &parsed_product_id)) {
return std::make_unique<DeviceId>(bus_type, parsed_vendor_id);
}
return std::make_unique<DeviceId>(bus_type, parsed_vendor_id,
parsed_product_id);
}
} // namespace
// static
std::unique_ptr<DeviceId> DeviceId::CreateFromSysfs(
const base::FilePath& syspath) {
if (syspath.empty()) {
return nullptr;
}
base::FilePath subsystem;
if (!base::ReadSymbolicLink(syspath.Append("subsystem"), &subsystem)) {
return nullptr;
}
std::string bus_type = subsystem.BaseName().value();
if (bus_type == "pci") {
return ReadDeviceId(DeviceId::BusType::kPci, syspath.Append("vendor"),
syspath.Append("product"));
} else if (bus_type == "usb") {
return ReadDeviceId(DeviceId::BusType::kUsb, syspath.Append("idVendor"),
syspath.Append("idProduct"));
}
return nullptr;
}
std::string DeviceId::AsString() const {
const char* bus_name;
switch (bus_type_) {
case BusType::kUsb:
bus_name = "usb";
break;
case BusType::kPci:
bus_name = "pci";
break;
}
if (!vendor_id_.has_value()) {
return base::StringPrintf("%s:*:*", bus_name);
}
if (!product_id_.has_value()) {
return base::StringPrintf("%s:%04" PRIx16 ":*", bus_name,
vendor_id_.value());
}
return base::StringPrintf("%s:%04" PRIx16 ":%04" PRIx16, bus_name,
vendor_id_.value(), product_id_.value());
}
bool DeviceId::Match(const DeviceId& pattern) const {
if (bus_type_ != pattern.bus_type_) {
return false;
}
// If |pattern| vendor id is *, then they don't have to match VID and PID
// values.
if (!pattern.vendor_id_.has_value()) {
return true;
}
// If |this| vendor id is *, then it can not match to |pattern| with specific
// vendor id.
if (!vendor_id_.has_value() ||
vendor_id_.value() != pattern.vendor_id_.value()) {
return false;
}
// If |pattern| product id is *, then they don't have to match PID values.
if (!pattern.product_id_.has_value()) {
return true;
}
// If |this| product id is *, then it can not match to |pattern| with specific
// product id.
return product_id_.has_value() &&
product_id_.value() == pattern.product_id_.value();
}
} // namespace shill
std::ostream& operator<<(std::ostream& stream,
const shill::DeviceId& device_id) {
return stream << device_id.AsString();
}