| // Copyright 2024 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "utils.hpp" |
| |
| #include <boost/asio.hpp> |
| #include <sdbusplus/asio/connection.hpp> |
| |
| #include <optional> |
| #include <set> |
| |
| extern "C" |
| { |
| #include <i2c/smbus.h> |
| #include <linux/i2c-dev.h> |
| } |
| |
| constexpr static char daemonType[] = "USBReset"; |
| |
| // a set of i2c<bus, addr> indicating if the i2c device has been intiated or not |
| // the set is used to ensure each USBHub just get reset once per power-up |
| std::set<std::pair<int, int>> initSet; |
| |
| static std::optional<int> |
| extractBusNumber(const std::string& path, |
| const SensorBaseConfigMap& properties) |
| { |
| auto findBus = properties.find("Bus"); |
| if (findBus == properties.end()) |
| { |
| std::cerr << "could not determine bus number for " << path << "\n"; |
| return std::nullopt; |
| } |
| |
| return std::visit(VariantToIntVisitor(), findBus->second); |
| } |
| |
| static std::optional<int> extractAddress(const std::string& path, |
| const SensorBaseConfigMap& properties) |
| { |
| auto findAddr = properties.find("Address"); |
| if (findAddr == properties.end()) |
| { |
| std::cerr << "could not determine address for " << path << "\n"; |
| return std::nullopt; |
| } |
| |
| return std::visit(VariantToIntVisitor(), findAddr->second); |
| } |
| |
| void resetUSBHub(int i2cBus, int i2cAddr) |
| { |
| std::cerr << "resetting i2c device: " << i2cBus << ", " << i2cAddr << '\n'; |
| |
| std::filesystem::path devpath = "/dev/i2c-" + std::to_string(i2cBus); |
| |
| int fd = ::open(devpath.c_str(), std::ios_base::in | std::ios_base::out); |
| if (fd < 0) { |
| std::cerr << "fail to open i2c device: " << i2cBus << ", " << i2cAddr |
| << '\n'; |
| return; |
| } |
| |
| /* Select the target device */ |
| // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) |
| if (::ioctl(fd, I2C_SLAVE_FORCE, i2cAddr) < 0) |
| { |
| std::cerr << "Failed to configure device address 0x" << std::hex |
| << i2cAddr << " for bus " << std::dec << i2cBus << ": " |
| << strerror(errno) << "\n"; |
| return; |
| } |
| |
| // retry max 5 times |
| int i = 0; |
| int res = -1; |
| for (; i < 5; i++) |
| { |
| if (i > 0) |
| { |
| std::cerr << "retry the reset command" << '\n'; |
| } |
| res = i2c_smbus_write_byte_data(fd, 0x01, 0x00); |
| if (res < 0) |
| { |
| std::cerr |
| << "Failed to set output register to all zero on address 0x" |
| << std::hex << i2cAddr << " for bus " << std::dec << i2cBus |
| << ": " << strerror(-res) << "\n"; |
| continue; |
| } |
| |
| res = i2c_smbus_write_byte_data(fd, 0x03, 0xfe); |
| if (res < 0) |
| { |
| std::cerr << "Failed to asserts USB Hub reset on address 0x" |
| << std::hex << i2cAddr << " for bus " << std::dec |
| << i2cBus << ": " << strerror(-res) << "\n"; |
| continue; |
| } |
| |
| std::this_thread::sleep_for(std::chrono::milliseconds(1)); |
| |
| res = i2c_smbus_write_byte_data(fd, 0x01, 0x01); |
| if (res < 0) |
| { |
| std::cerr << "Failed to deasserts USB Hub reset on address 0x" |
| << std::hex << i2cAddr << " for bus " << std::dec |
| << i2cBus << ": " << strerror(-res) << "\n"; |
| continue; |
| } |
| break; |
| } |
| |
| if (i >= 5) |
| { |
| std::cerr << "Failed to send reset command to address 0x" << std::hex |
| << i2cAddr << " for bus " << std::dec << i2cBus << "\n"; |
| } |
| |
| ::close(fd); |
| } |
| |
| void handler(const std::shared_ptr<sdbusplus::asio::connection> &bus) |
| { |
| |
| auto getter = std::make_shared<GetSensorConfiguration>( |
| bus, [](const ManagedObjectType &configurations) |
| { |
| if (!readingStateGood(PowerState::on)) |
| { |
| return; |
| } |
| |
| for (const auto &[path, configData] : configurations) |
| { |
| auto find = configData.find(configInterfaceName(daemonType)); |
| if (find == configData.end()) |
| { |
| continue; |
| } |
| const auto &config = find->second; |
| auto i2cBus = extractBusNumber(path, config); |
| auto i2cAddr = extractAddress(path, config); |
| |
| if (!i2cBus || !i2cAddr) |
| { |
| std::cerr << "failed to find i2c bus info: continue" << '\n'; |
| continue; |
| } |
| auto initiated = initSet.find({*i2cBus, *i2cAddr}); |
| if (initiated != initSet.end()) |
| { |
| continue; |
| } |
| resetUSBHub(*i2cBus, *i2cAddr); |
| initSet.emplace(*i2cBus, *i2cAddr); |
| } |
| }); |
| getter->getConfiguration(std::vector<std::string>{daemonType}); |
| } |
| |
| int main() |
| { |
| boost::asio::io_service io; |
| auto systemBus = std::make_shared<sdbusplus::asio::connection>(io); |
| sdbusplus::asio::object_server objectServer(systemBus, true); |
| |
| [[maybe_unused]] auto powerCb = |
| setupPowerMatchCallback(systemBus, [&](PowerState type, bool state) { |
| if (type != PowerState::on) |
| { |
| return; |
| } |
| |
| // clear the initSet when the Host power is done |
| if (!state) |
| { |
| initSet.clear(); |
| } |
| else |
| { |
| handler(systemBus); |
| } |
| }); |
| |
| std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches = |
| setupPropertiesChangedMatches( |
| *systemBus, std::to_array<const char*>({daemonType}), |
| [&](sdbusplus::message_t&) { |
| static boost::asio::steady_timer timer(io); |
| timer.expires_from_now(std::chrono::seconds(1)); |
| timer.async_wait([&](const boost::system::error_code& ec) { |
| if (ec == boost::asio::error::operation_aborted) |
| { |
| return; // we're being canceled |
| } |
| |
| if (ec) |
| { |
| std::cerr << "Error: " << ec.message() << "\n"; |
| return; |
| } |
| |
| handler(systemBus); |
| }); |
| }); |
| boost::asio::post(io, [systemBus] { handler(systemBus); }); |
| io.run(); |
| } |