1 /**
2 * Copyright © 2021 IBM Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "i2c_capture_bytes_action.hpp"
18
19 #include "action_error.hpp"
20 #include "i2c_interface.hpp"
21
22 #include <exception>
23 #include <ios>
24 #include <sstream>
25
26 namespace phosphor::power::regulators
27 {
28
execute(ActionEnvironment & environment)29 bool I2CCaptureBytesAction::execute(ActionEnvironment& environment)
30 {
31 try
32 {
33 // Read device register values. Use I2C mode where the number of bytes
34 // to read is explicitly specified.
35 i2c::I2CInterface& interface = getI2CInterface(environment);
36 uint8_t size{count}; // byte count parameter is input/output
37 uint8_t values[UINT8_MAX];
38 interface.read(reg, size, values, i2c::I2CInterface::Mode::I2C);
39
40 // Store error data in action environment as a string key/value pair
41 std::string key = getErrorDataKey(environment);
42 std::string value = getErrorDataValue(values);
43 environment.addAdditionalErrorData(key, value);
44 }
45 catch (const i2c::I2CException& e)
46 {
47 // Nest I2CException within an ActionError so caller will have both the
48 // low level I2C error information and the action information
49 std::throw_with_nested(ActionError(*this));
50 }
51 return true;
52 }
53
toString() const54 std::string I2CCaptureBytesAction::toString() const
55 {
56 std::ostringstream ss;
57 ss << "i2c_capture_bytes: { register: 0x" << std::hex << std::uppercase
58 << static_cast<uint16_t>(reg) << ", count: " << std::dec
59 << static_cast<uint16_t>(count) << " }";
60 return ss.str();
61 }
62
63 std::string
getErrorDataKey(ActionEnvironment & environment) const64 I2CCaptureBytesAction::getErrorDataKey(ActionEnvironment& environment) const
65 {
66 // Additional error data key format: <deviceID>_register_<register>
67 std::ostringstream ss;
68 ss << environment.getDeviceID() << "_register_0x" << std::hex
69 << std::uppercase << static_cast<uint16_t>(reg);
70 std::string key = ss.str();
71
72 // Verify key does not already exist in the action environment. This occurs
73 // when the same device and register is captured multiple times.
74 if (environment.getAdditionalErrorData().contains(key))
75 {
76 // Add counter suffix to key and loop until unused key is found
77 int counter = 2;
78 std::string keyWithSuffix;
79 do
80 {
81 keyWithSuffix = key + '_' + std::to_string(counter);
82 if (!environment.getAdditionalErrorData().contains(keyWithSuffix))
83 {
84 key = keyWithSuffix;
85 break;
86 }
87 ++counter;
88 } while (true);
89 }
90
91 return key;
92 }
93
94 std::string
getErrorDataValue(const uint8_t * values) const95 I2CCaptureBytesAction::getErrorDataValue(const uint8_t* values) const
96 {
97 // Additional error data value format: [ <byte 0>, <byte 1>, ... ]
98 std::ostringstream ss;
99 ss << "[ " << std::hex << std::uppercase;
100 for (unsigned int i = 0; i < count; ++i)
101 {
102 ss << ((i > 0) ? ", " : "") << "0x" << static_cast<uint16_t>(values[i]);
103 }
104 ss << " ]";
105 return ss.str();
106 }
107
108 } // namespace phosphor::power::regulators
109