1 #include "config.h"
2 
3 #include "i2c_occ.hpp"
4 
5 #include <algorithm>
6 #include <cassert>
7 #include <fstream>
8 
9 #ifdef I2C_OCC
10 
11 namespace i2c_occ
12 {
13 
14 namespace fs = std::filesystem;
15 
16 // The occ_master sysfs file
17 constexpr auto OCC_MASTER_FILE = "occ_master";
18 // The device name's length, e.g. "p8-occ-hwmon"
19 constexpr auto DEVICE_NAME_LENGTH = 12;
20 // The occ name's length, e.g. "occ"
21 constexpr auto OCC_NAME_LENGTH = 3;
22 
23 // static assert to make sure the i2c occ device name is expected
24 static_assert(sizeof(I2C_OCC_DEVICE_NAME) - 1 == DEVICE_NAME_LENGTH);
25 static_assert(sizeof(OCC_NAME) - 1 == OCC_NAME_LENGTH);
26 
isMasterOcc(const fs::directory_entry & p)27 static bool isMasterOcc(const fs::directory_entry& p)
28 {
29     auto f = p / fs::path{OCC_MASTER_FILE};
30     auto str = getFileContent(f);
31     return (!str.empty()) && (str[0] == '1');
32 }
33 
getFileContent(const fs::path & f)34 std::string getFileContent(const fs::path& f)
35 {
36     std::string ret(DEVICE_NAME_LENGTH, 0);
37     std::ifstream ifs(f.c_str(), std::ios::binary);
38     if (ifs.is_open())
39     {
40         ifs.read(&ret[0], DEVICE_NAME_LENGTH);
41         ret.resize(ifs.gcount());
42     }
43     return ret;
44 }
45 
getOccHwmonDevices(const char * path)46 std::vector<std::string> getOccHwmonDevices(const char* path)
47 {
48     std::vector<std::string> result{};
49 
50     if (fs::is_directory(path))
51     {
52         for (auto& p : fs::directory_iterator(path))
53         {
54             // Check if a device's name is "p8-occ-hwmon"
55             auto f = p / fs::path{"name"};
56             auto str = getFileContent(f);
57             if (str == I2C_OCC_DEVICE_NAME)
58             {
59                 if (isMasterOcc(p))
60                 {
61                     // Insert master occ at the beginning
62                     result.emplace(result.begin(), p.path().filename());
63                 }
64                 else
65                 {
66                     result.emplace_back(p.path().filename());
67                 }
68             }
69         }
70     }
71     if (!result.empty())
72     {
73         // Sort the occ devices except for master
74         std::sort(result.begin() + 1, result.end());
75     }
76     return result;
77 }
78 
i2cToDbus(std::string & path)79 void i2cToDbus(std::string& path)
80 {
81     std::replace(path.begin(), path.end(), '-', '_');
82 }
83 
dbusToI2c(std::string & path)84 void dbusToI2c(std::string& path)
85 {
86     std::replace(path.begin(), path.end(), '_', '-');
87 }
88 
getI2cDeviceName(const std::string & dbusPath)89 std::string getI2cDeviceName(const std::string& dbusPath)
90 {
91     auto name = fs::path(dbusPath).filename().string();
92 
93     // Need to make sure the name starts with "occ"
94     assert(name.compare(0, OCC_NAME_LENGTH, OCC_NAME) == 0);
95 
96     // Change name like occ_3_0050 to 3_0050
97     name.erase(0, OCC_NAME_LENGTH + 1);
98 
99     dbusToI2c(name);
100     return name;
101 }
102 
103 } // namespace i2c_occ
104 
105 #endif
106