1 /**
2 * Copyright © 2019 Facebook
3 * Copyright © 2023 9elements GmbH
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 #include "gpio_presence.hpp"
19
20 #include <CLI/CLI.hpp>
21 #include <boost/asio/io_context.hpp>
22 #include <nlohmann/json.hpp>
23 #include <phosphor-logging/lg2.hpp>
24
25 #include <fstream>
26
27 namespace phosphor
28 {
29 namespace gpio
30 {
31
32 const std::map<std::string, int> biasMap = {
33 /**< Set bias as is. */
34 {"AS_IS", 0},
35 /**< Disable bias. */
36 {"DISABLE", GPIOD_LINE_REQUEST_FLAG_BIAS_DISABLE},
37 /**< Enable pull-up. */
38 {"PULL_UP", GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP},
39 /**< Enable pull-down. */
40 {"PULL_DOWN", GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN}};
41 }
42 } // namespace phosphor
43
main(int argc,char ** argv)44 int main(int argc, char** argv)
45 {
46 boost::asio::io_context io;
47
48 CLI::App app{"Monitor gpio presence status"};
49
50 std::string gpioFileName;
51
52 /* Add an input option */
53 app.add_option("-c,--config", gpioFileName, "Name of config json file")
54 ->required()
55 ->check(CLI::ExistingFile);
56
57 /* Parse input parameter */
58 try
59 {
60 app.parse(argc, argv);
61 }
62 catch (const CLI::Error& e)
63 {
64 return app.exit(e);
65 }
66
67 /* Get list of gpio config details from json file */
68 std::ifstream file(gpioFileName);
69 if (!file)
70 {
71 lg2::error("Failed to open config file: {FILE}", "FILE", gpioFileName);
72 return -1;
73 }
74
75 nlohmann::json gpioMonObj;
76 file >> gpioMonObj;
77 file.close();
78
79 std::vector<phosphor::gpio::GpioPresence> gpios;
80
81 for (auto& obj : gpioMonObj)
82 {
83 /* GPIO Line message */
84 std::string lineMsg = "GPIO Line ";
85
86 /* GPIO line */
87 gpiod_line* line = nullptr;
88
89 /* GPIO line configuration, default to monitor both edge */
90 struct gpiod_line_request_config config{
91 "gpio_monitor", GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES, 0};
92
93 /* Pretty name of the inventory object */
94 std::string name;
95
96 /* Object path under inventory that will be created */
97 std::string inventory;
98
99 /* List of interfaces to associate to inventory item */
100 std::vector<std::string> extraInterfaces;
101
102 if (obj.find("LineName") == obj.end())
103 {
104 /* If there is no line Name defined then gpio num nd chip
105 * id must be defined. GpioNum is integer mapping to the
106 * GPIO key configured by the kernel
107 */
108 if (obj.find("GpioNum") == obj.end() ||
109 obj.find("ChipId") == obj.end())
110 {
111 lg2::error("Failed to find line name or gpio number: {FILE}",
112 "FILE", gpioFileName);
113 return -1;
114 }
115
116 std::string chipIdStr = obj["ChipId"].get<std::string>();
117 int gpioNum = obj["GpioNum"].get<int>();
118
119 lineMsg += chipIdStr + " " + std::to_string(gpioNum);
120
121 /* Get the GPIO line */
122 line = gpiod_line_get(chipIdStr.c_str(), gpioNum);
123 }
124 else
125 {
126 /* Find the GPIO line */
127 std::string lineName = obj["LineName"].get<std::string>();
128 lineMsg += lineName;
129 line = gpiod_line_find(lineName.c_str());
130 }
131
132 if (line == nullptr)
133 {
134 lg2::error("Failed to find the {GPIO}", "GPIO", lineMsg);
135 continue;
136 }
137
138 /* Parse out inventory argument. */
139 if (obj.find("Inventory") == obj.end())
140 {
141 lg2::error("{GPIO}: Inventory path not specified", "GPIO", lineMsg);
142 return -1;
143 }
144 else
145 {
146 inventory = obj["Inventory"].get<std::string>();
147 }
148
149 if (obj.find("Name") == obj.end())
150 {
151 lg2::error("{GPIO}: Name path not specified", "GPIO", lineMsg);
152 return -1;
153 }
154 else
155 {
156 name = obj["Name"].get<std::string>();
157 }
158
159 /* Parse optional bias */
160 if (obj.find("Bias") != obj.end())
161 {
162 std::string biasName = obj["Bias"].get<std::string>();
163 auto findBias = phosphor::gpio::biasMap.find(biasName);
164 if (findBias == phosphor::gpio::biasMap.end())
165 {
166 lg2::error("{GPIO}: Bias unknown: {BIAS}", "GPIO", lineMsg,
167 "BIAS", biasName);
168 return -1;
169 }
170
171 config.flags = findBias->second;
172 }
173
174 /* Parse optional active level */
175 if (obj.find("ActiveLow") != obj.end() && obj["ActiveLow"].get<bool>())
176 {
177 config.flags |= GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW;
178 }
179
180 /* Parse optional extra interfaces */
181 if (obj.find("ExtraInterfaces") != obj.end())
182 {
183 obj.at("ExtraInterfaces").get_to(extraInterfaces);
184 }
185
186 /* Create a monitor object and let it do all the rest */
187 gpios.push_back(phosphor::gpio::GpioPresence(
188 line, config, io, inventory, extraInterfaces, name, lineMsg));
189 }
190 io.run();
191
192 return 0;
193 }
194