| // 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(); |
| } |