1 #include "config-validator.hpp"
2 
3 #include "grouplayout.hpp"
4 #include "ledlayout.hpp"
5 
6 #include <phosphor-logging/lg2.hpp>
7 #include <sdbusplus/bus.hpp>
8 
9 using namespace phosphor::led;
10 
11 namespace phosphor
12 {
13 namespace led
14 {
15 
16 // Priority for a particular LED needs to stay SAME across all groups
17 using PriorityMap =
18     std::unordered_map<std::string,
19                        std::optional<phosphor::led::Layout::Action>>;
20 
isUsingGroupPriority(const phosphor::led::GroupMap & ledMap)21 static bool isUsingGroupPriority(const phosphor::led::GroupMap& ledMap)
22 {
23     for (const auto& [_, group] : ledMap)
24     {
25         if (group.priority != 0)
26         {
27             return true;
28         }
29     }
30     return false;
31 }
32 
priorityToString(const std::optional<phosphor::led::Layout::Action> & priority)33 static std::string priorityToString(
34     const std::optional<phosphor::led::Layout::Action>& priority)
35 {
36     if (!priority.has_value())
37     {
38         return "none";
39     }
40     switch (priority.value())
41     {
42         case phosphor::led::Layout::Action::Off:
43             return "Off";
44         case phosphor::led::Layout::Action::On:
45             return "On";
46         case phosphor::led::Layout::Action::Blink:
47             return "Blink";
48     }
49     return "?";
50 }
51 
52 /** @brief Validate the Priority of an LED is same across ALL groups
53  *
54  *  @param[in] name - led name member of each group
55  *  @param[in] priority - member priority of each group
56  *  @param[out] priorityMap - std::unordered_map, key:name, value:priority
57  *
58  *  @return
59  */
validatePriority(const std::string & name,const std::optional<phosphor::led::Layout::Action> & priority,PriorityMap & priorityMap)60 void validatePriority(
61     const std::string& name,
62     const std::optional<phosphor::led::Layout::Action>& priority,
63     PriorityMap& priorityMap)
64 {
65     auto iter = priorityMap.find(name);
66     if (iter == priorityMap.end())
67     {
68         priorityMap.emplace(name, priority);
69         return;
70     }
71 
72     if (iter->second != priority)
73     {
74         throw ConfigValidationException(
75             error::LedPriorityMismatch, "?", name,
76             "Priority of the LED is not same across groups. Old Priority = " +
77                 priorityToString(iter->second) +
78                 ", New Priority = " + priorityToString(priority));
79     }
80 }
81 
validateConfigV1GroupForLedPriority(const std::string groupName,const phosphor::led::Layout::GroupLayout & group,PriorityMap & priorityMap)82 static void validateConfigV1GroupForLedPriority(
83     const std::string groupName,
84     const phosphor::led::Layout::GroupLayout& group, PriorityMap& priorityMap)
85 {
86     if (group.priority != 0)
87     {
88         throw ConfigValidationException(
89             error::MixedLedAndGroupPriority, groupName,
90             "Cannot mix group priority and led priority");
91     }
92 
93     for (const auto& ledAction : group.actionSet)
94     {
95         if (ledAction.priority == std::nullopt)
96         {
97             throw ConfigValidationException(error::MissingLedPriority,
98                                             groupName, ledAction.name,
99                                             "Need valid led priority");
100         }
101 
102         // Same LEDs can be part of multiple groups. However, their
103         // priorities across groups need to match.
104         validatePriority(ledAction.name, ledAction.priority, priorityMap);
105     }
106 }
107 
108 static void
validateConfigV1ForLedPriority(const phosphor::led::GroupMap & ledMap)109     validateConfigV1ForLedPriority(const phosphor::led::GroupMap& ledMap)
110 {
111     PriorityMap priorityMap{};
112     for (const auto& [groupName, group] : ledMap)
113     {
114         validateConfigV1GroupForLedPriority(groupName, group, priorityMap);
115     }
116 }
117 
validateConfigV1GroupForGroupPriority(const std::string groupName,const phosphor::led::Layout::GroupLayout & group)118 static void validateConfigV1GroupForGroupPriority(
119     const std::string groupName,
120     const phosphor::led::Layout::GroupLayout& group)
121 {
122     for (const auto& led : group.actionSet)
123     {
124         if (led.priority != std::nullopt)
125         {
126             throw ConfigValidationException(
127                 error::MixedLedAndGroupPriority, groupName, led.name,
128                 "Cannot mix group priority and led priority for LED");
129         }
130     }
131 
132     if (group.priority == 0)
133     {
134         // group priority 0 is for internal use
135         throw ConfigValidationException(error::InvalidGroupPriority, groupName,
136                                         "Group Priority cannot be 0");
137     }
138 }
139 
140 static void
validateConfigV1ForGroupPriority(const phosphor::led::GroupMap & ledMap)141     validateConfigV1ForGroupPriority(const phosphor::led::GroupMap& ledMap)
142 {
143     std::set<int> groupPriorities;
144     for (const auto& [_, group] : ledMap)
145     {
146         groupPriorities.insert(group.priority);
147     }
148 
149     if (groupPriorities.size() != ledMap.size())
150     {
151         throw ConfigValidationException(
152             error::DuplicateGroupPriority,
153             "When using Group Priority, no 2 Groups may have the same priority");
154     }
155 
156     for (const auto& [groupName, group] : ledMap)
157     {
158         validateConfigV1GroupForGroupPriority(groupName, group);
159     }
160 }
161 
validateConfigV1(const GroupMap & ledMap)162 void validateConfigV1(const GroupMap& ledMap)
163 {
164     const bool useGroupPriority = isUsingGroupPriority(ledMap);
165 
166     if (useGroupPriority)
167     {
168         validateConfigV1ForGroupPriority(ledMap);
169     }
170     else
171     {
172         validateConfigV1ForLedPriority(ledMap);
173     }
174 }
175 
176 } // namespace led
177 } // namespace phosphor
178