| /** | 
 |  * Copyright © 2019 IBM Corporation | 
 |  * | 
 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 |  * you may not use this file except in compliance with the License. | 
 |  * You may obtain a copy of the License at | 
 |  * | 
 |  *     http://www.apache.org/licenses/LICENSE-2.0 | 
 |  * | 
 |  * Unless required by applicable law or agreed to in writing, software | 
 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 |  * See the License for the specific language governing permissions and | 
 |  * limitations under the License. | 
 |  */ | 
 | #include "fru_identity.hpp" | 
 |  | 
 | #include "pel_values.hpp" | 
 |  | 
 | #include <fmt/format.h> | 
 |  | 
 | #include <phosphor-logging/log.hpp> | 
 |  | 
 | using namespace phosphor::logging; | 
 |  | 
 | namespace openpower | 
 | { | 
 | namespace pels | 
 | { | 
 | namespace src | 
 | { | 
 |  | 
 | namespace | 
 | { | 
 |  | 
 | /** | 
 |  * @brief Fills in the std::array from the string value | 
 |  * | 
 |  * If the string is shorter than the array, it will be padded with | 
 |  * '\0's. | 
 |  * | 
 |  * @param[in] source - The string to fill in the array with | 
 |  * @param[out] target - The input object that supports [] and size() | 
 |  */ | 
 | template <typename T> | 
 | void fillArray(const std::string& source, T& target) | 
 | { | 
 |     for (size_t i = 0; i < target.size(); i++) | 
 |     { | 
 |         target[i] = (source.size() > i) ? source[i] : '\0'; | 
 |     } | 
 | } | 
 |  | 
 | } // namespace | 
 |  | 
 | FRUIdentity::FRUIdentity(Stream& pel) | 
 | { | 
 |     pel >> _type >> _size >> _flags; | 
 |  | 
 |     if (hasPN() || hasMP()) | 
 |     { | 
 |         pel.read(_pnOrProcedureID.data(), _pnOrProcedureID.size()); | 
 |     } | 
 |  | 
 |     if (hasCCIN()) | 
 |     { | 
 |         pel.read(_ccin.data(), _ccin.size()); | 
 |     } | 
 |  | 
 |     if (hasSN()) | 
 |     { | 
 |         pel.read(_sn.data(), _sn.size()); | 
 |     } | 
 | } | 
 |  | 
 | size_t FRUIdentity::flattenedSize() const | 
 | { | 
 |     size_t size = sizeof(_type) + sizeof(_size) + sizeof(_flags); | 
 |  | 
 |     if (hasPN() || hasMP()) | 
 |     { | 
 |         size += _pnOrProcedureID.size(); | 
 |     } | 
 |  | 
 |     if (hasCCIN()) | 
 |     { | 
 |         size += _ccin.size(); | 
 |     } | 
 |  | 
 |     if (hasSN()) | 
 |     { | 
 |         size += _sn.size(); | 
 |     } | 
 |  | 
 |     return size; | 
 | } | 
 |  | 
 | FRUIdentity::FRUIdentity(const std::string& partNumber, const std::string& ccin, | 
 |                          const std::string& serialNumber) | 
 | { | 
 |     _type = substructureType; | 
 |     _flags = hardwareFRU; | 
 |  | 
 |     setPartNumber(partNumber); | 
 |     setCCIN(ccin); | 
 |     setSerialNumber(serialNumber); | 
 |  | 
 |     _size = flattenedSize(); | 
 | } | 
 |  | 
 | FRUIdentity::FRUIdentity(const std::string& procedure, CalloutValueType type) | 
 | { | 
 |     _type = substructureType; | 
 |     _flags = maintenanceProc; | 
 |  | 
 |     setMaintenanceProcedure(procedure, type); | 
 |  | 
 |     _size = flattenedSize(); | 
 | } | 
 |  | 
 | FRUIdentity::FRUIdentity(const std::string& fru, CalloutValueType type, | 
 |                          bool trustedLocationCode) | 
 | { | 
 |     _type = substructureType; | 
 |     _flags = (trustedLocationCode) ? symbolicFRUTrustedLocCode : symbolicFRU; | 
 |  | 
 |     setSymbolicFRU(fru, type); | 
 |  | 
 |     _size = flattenedSize(); | 
 | } | 
 |  | 
 | std::optional<std::string> FRUIdentity::getPN() const | 
 | { | 
 |     if (hasPN()) | 
 |     { | 
 |         // NULL terminated | 
 |         std::string pn{_pnOrProcedureID.data()}; | 
 |         return pn; | 
 |     } | 
 |  | 
 |     return std::nullopt; | 
 | } | 
 |  | 
 | std::optional<std::string> FRUIdentity::getMaintProc() const | 
 | { | 
 |     if (hasMP()) | 
 |     { | 
 |         // NULL terminated | 
 |         std::string mp{_pnOrProcedureID.data()}; | 
 |         return mp; | 
 |     } | 
 |  | 
 |     return std::nullopt; | 
 | } | 
 |  | 
 | std::optional<std::string> FRUIdentity::getCCIN() const | 
 | { | 
 |     if (hasCCIN()) | 
 |     { | 
 |         std::string ccin{_ccin.begin(), _ccin.begin() + _ccin.size()}; | 
 |  | 
 |         // Don't leave any NULLs in the string (not there usually) | 
 |         if (auto pos = ccin.find('\0'); pos != std::string::npos) | 
 |         { | 
 |             ccin.resize(pos); | 
 |         } | 
 |         return ccin; | 
 |     } | 
 |  | 
 |     return std::nullopt; | 
 | } | 
 |  | 
 | std::optional<std::string> FRUIdentity::getSN() const | 
 | { | 
 |     if (hasSN()) | 
 |     { | 
 |         std::string sn{_sn.begin(), _sn.begin() + _sn.size()}; | 
 |  | 
 |         // Don't leave any NULLs in the string (not there usually) | 
 |         if (auto pos = sn.find('\0'); pos != std::string::npos) | 
 |         { | 
 |             sn.resize(pos); | 
 |         } | 
 |         return sn; | 
 |     } | 
 |  | 
 |     return std::nullopt; | 
 | } | 
 |  | 
 | void FRUIdentity::flatten(Stream& pel) const | 
 | { | 
 |     pel << _type << _size << _flags; | 
 |  | 
 |     if (hasPN() || hasMP()) | 
 |     { | 
 |         pel.write(_pnOrProcedureID.data(), _pnOrProcedureID.size()); | 
 |     } | 
 |  | 
 |     if (hasCCIN()) | 
 |     { | 
 |         pel.write(_ccin.data(), _ccin.size()); | 
 |     } | 
 |  | 
 |     if (hasSN()) | 
 |     { | 
 |         pel.write(_sn.data(), _sn.size()); | 
 |     } | 
 | } | 
 |  | 
 | void FRUIdentity::setPartNumber(const std::string& partNumber) | 
 | { | 
 |     _flags |= pnSupplied; | 
 |     _flags &= ~maintProcSupplied; | 
 |  | 
 |     auto pn = partNumber; | 
 |  | 
 |     // Strip leading whitespace on this one. | 
 |     while (' ' == pn.front()) | 
 |     { | 
 |         pn = pn.substr(1); | 
 |     } | 
 |  | 
 |     fillArray(pn, _pnOrProcedureID); | 
 |  | 
 |     // ensure null terminated | 
 |     _pnOrProcedureID.back() = 0; | 
 | } | 
 |  | 
 | void FRUIdentity::setCCIN(const std::string& ccin) | 
 | { | 
 |     _flags |= ccinSupplied; | 
 |  | 
 |     fillArray(ccin, _ccin); | 
 | } | 
 |  | 
 | void FRUIdentity::setSerialNumber(const std::string& serialNumber) | 
 | { | 
 |     _flags |= snSupplied; | 
 |  | 
 |     fillArray(serialNumber, _sn); | 
 | } | 
 |  | 
 | void FRUIdentity::setMaintenanceProcedure(const std::string& procedure, | 
 |                                           CalloutValueType type) | 
 | { | 
 |     _flags |= maintProcSupplied; | 
 |     _flags &= ~pnSupplied; | 
 |  | 
 |     if (type == CalloutValueType::registryName) | 
 |     { | 
 |         if (pel_values::maintenanceProcedures.count(procedure)) | 
 |         { | 
 |             fillArray(pel_values::maintenanceProcedures.at(procedure), | 
 |                       _pnOrProcedureID); | 
 |         } | 
 |         else | 
 |         { | 
 |             log<level::ERR>( | 
 |                 fmt::format("Invalid maintenance procedure {}", procedure) | 
 |                     .c_str()); | 
 |             strncpy(_pnOrProcedureID.data(), "INVALID", | 
 |                     _pnOrProcedureID.size()); | 
 |         } | 
 |     } | 
 |     else | 
 |     { | 
 |         fillArray(procedure, _pnOrProcedureID); | 
 |     } | 
 |  | 
 |     // ensure null terminated | 
 |     _pnOrProcedureID.back() = 0; | 
 | } | 
 |  | 
 | void FRUIdentity::setSymbolicFRU(const std::string& symbolicFRU, | 
 |                                  CalloutValueType type) | 
 | { | 
 |     // Treat this has a HW callout. | 
 |     _flags |= pnSupplied; | 
 |     _flags &= ~maintProcSupplied; | 
 |  | 
 |     if (type == CalloutValueType::registryName) | 
 |     { | 
 |         if (pel_values::symbolicFRUs.count(symbolicFRU)) | 
 |         { | 
 |             fillArray(pel_values::symbolicFRUs.at(symbolicFRU), | 
 |                       _pnOrProcedureID); | 
 |         } | 
 |         else | 
 |         { | 
 |             log<level::ERR>("Invalid symbolic FRU", | 
 |                             entry("FRU=%s", symbolicFRU.c_str())); | 
 |             strncpy(_pnOrProcedureID.data(), "INVALID", | 
 |                     _pnOrProcedureID.size()); | 
 |         } | 
 |     } | 
 |     else | 
 |     { | 
 |         fillArray(symbolicFRU, _pnOrProcedureID); | 
 |     } | 
 |  | 
 |     // ensure null terminated | 
 |     _pnOrProcedureID.back() = 0; | 
 | } | 
 |  | 
 | } // namespace src | 
 | } // namespace pels | 
 | } // namespace openpower |