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 = NULL;
88 
89         /* GPIO line configuration, default to monitor both edge */
90         struct gpiod_line_request_config config
91         {
92             "gpio_monitor", GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES, 0
93         };
94 
95         /* Pretty name of the inventory object */
96         std::string name;
97 
98         /* Object path under inventory that will be created */
99         std::string inventory;
100 
101         /* List of interfaces to associate to inventory item */
102         std::vector<std::string> extraInterfaces;
103 
104         if (obj.find("LineName") == obj.end())
105         {
106             /* If there is no line Name defined then gpio num nd chip
107              * id must be defined. GpioNum is integer mapping to the
108              * GPIO key configured by the kernel
109              */
110             if (obj.find("GpioNum") == obj.end() ||
111                 obj.find("ChipId") == obj.end())
112             {
113                 lg2::error("Failed to find line name or gpio number: {FILE}",
114                            "FILE", gpioFileName);
115                 return -1;
116             }
117 
118             std::string chipIdStr = obj["ChipId"].get<std::string>();
119             int gpioNum = obj["GpioNum"].get<int>();
120 
121             lineMsg += chipIdStr + " " + std::to_string(gpioNum);
122 
123             /* Get the GPIO line */
124             line = gpiod_line_get(chipIdStr.c_str(), gpioNum);
125         }
126         else
127         {
128             /* Find the GPIO line */
129             std::string lineName = obj["LineName"].get<std::string>();
130             lineMsg += lineName;
131             line = gpiod_line_find(lineName.c_str());
132         }
133 
134         if (line == NULL)
135         {
136             lg2::error("Failed to find the {GPIO}", "GPIO", lineMsg);
137             return -1;
138         }
139 
140         /* Parse out inventory argument. */
141         if (obj.find("Inventory") == obj.end())
142         {
143             lg2::error("{GPIO}: Inventory path not specified", "GPIO", lineMsg);
144             return -1;
145         }
146         else
147         {
148             inventory = obj["Inventory"].get<std::string>();
149         }
150 
151         if (obj.find("Name") == obj.end())
152         {
153             lg2::error("{GPIO}: Name path not specified", "GPIO", lineMsg);
154             return -1;
155         }
156         else
157         {
158             name = obj["Name"].get<std::string>();
159         }
160 
161         /* Parse optional bias */
162         if (obj.find("Bias") != obj.end())
163         {
164             std::string biasName = obj["Bias"].get<std::string>();
165             auto findBias = phosphor::gpio::biasMap.find(biasName);
166             if (findBias == phosphor::gpio::biasMap.end())
167             {
168                 lg2::error("{GPIO}: Bias unknown: {BIAS}", "GPIO", lineMsg,
169                            "BIAS", biasName);
170                 return -1;
171             }
172 
173             config.flags = findBias->second;
174         }
175 
176         /* Parse optional active level */
177         if (obj.find("ActiveLow") != obj.end() && obj["ActiveLow"].get<bool>())
178         {
179             config.flags |= GPIOD_LINE_REQUEST_FLAG_ACTIVE_LOW;
180         }
181 
182         /* Parse optional extra interfaces */
183         if (obj.find("ExtraInterfaces") != obj.end())
184         {
185             obj.at("ExtraInterfaces").get_to(extraInterfaces);
186         }
187 
188         /* Create a monitor object and let it do all the rest */
189         gpios.push_back(phosphor::gpio::GpioPresence(
190             line, config, io, inventory, extraInterfaces, name, lineMsg));
191     }
192     io.run();
193 
194     return 0;
195 }
196