Add a helper function findFruAreaLocationAndField to
updateFRUProperty

Refactoring updateFRUProperty function and created a new helper
function findFruAreaLocationAndField. Moved this function to
fru_utils.cpp as it is common for all fru-device deamons and
avoid code duplication.

This patch is created based on suggestion on the below patch.
https://gerrit.openbmc.org/c/openbmc/entity-manager/+/51555

TESTED : Built Facebook YosemiteV2 images and loaded
on the target hardware. Verified all the fru's read and write.

Signed-off-by: Kumar Thangavel <thangavel.k@hcl.com>
Change-Id: I82265e66ea5db10003e96978979250ea45c2052d
diff --git a/include/fru_utils.hpp b/include/fru_utils.hpp
index e50b580..f1d8e94 100644
--- a/include/fru_utils.hpp
+++ b/include/fru_utils.hpp
@@ -62,6 +62,14 @@
     fruAreaMultirecord
 };
 
+struct FruArea
+{
+    size_t start;          // Fru Area Start offset
+    size_t size;           // Fru Area Size
+    size_t end;            // Fru Area end offset
+    size_t updateFieldLoc; // Fru Area update Field Location
+};
+
 const std::vector<std::string> fruAreaNames = {"INTERNAL", "CHASSIS", "BOARD",
                                                "PRODUCT", "MULTIRECORD"};
 const std::regex nonAsciiRegex("[^\x01-\x7f]");
@@ -157,3 +165,15 @@
 /// \param area - the area
 /// \return the field offset
 unsigned int getHeaderAreaFieldOffset(fruAreas area);
+
+/// \brief Iterate fruArea Names and find offset/location and fields and size of
+/// properties
+/// \param fruData - vector to store fru data
+/// \param propertyName - fru property Name
+/// \param fruAreaParams - struct to have fru Area paramteters like length,
+/// size. \return true if fru field is found, fruAreaParams are updated with
+/// fruArea and field info.
+bool findFruAreaLocationAndField(std::vector<uint8_t>& fruData,
+                                 const std::string& propertyName,
+                                 struct FruArea& fruAreaParams,
+                                 size_t& fruDataIter);
diff --git a/src/fru_device.cpp b/src/fru_device.cpp
index ed55e4a..50f1e4b 100644
--- a/src/fru_device.cpp
+++ b/src/fru_device.cpp
@@ -1108,100 +1108,20 @@
         return false;
     }
 
-    const std::vector<std::string>* fruAreaFieldNames = nullptr;
+    struct FruArea fruAreaParams{};
+    size_t fruDataIter = 0;
 
-    uint8_t fruAreaOffsetFieldValue = 0;
-    size_t offset = 0;
-    std::string areaName = propertyName.substr(0, propertyName.find('_'));
-    std::string propertyNamePrefix = areaName + "_";
-    auto it = std::find(fruAreaNames.begin(), fruAreaNames.end(), areaName);
-    if (it == fruAreaNames.end())
+    if (!findFruAreaLocationAndField(fruData, propertyName, fruAreaParams,
+                                     fruDataIter))
     {
-        std::cerr << "Can't parse area name for property " << propertyName
-                  << " \n";
-        return false;
-    }
-    fruAreas fruAreaToUpdate = static_cast<fruAreas>(it - fruAreaNames.begin());
-    fruAreaOffsetFieldValue =
-        fruData[getHeaderAreaFieldOffset(fruAreaToUpdate)];
-    switch (fruAreaToUpdate)
-    {
-        case fruAreas::fruAreaChassis:
-            offset = 3; // chassis part number offset. Skip fixed first 3 bytes
-            fruAreaFieldNames = &chassisFruAreas;
-            break;
-        case fruAreas::fruAreaBoard:
-            offset = 6; // board manufacturer offset. Skip fixed first 6 bytes
-            fruAreaFieldNames = &boardFruAreas;
-            break;
-        case fruAreas::fruAreaProduct:
-            // Manufacturer name offset. Skip fixed first 3 product fru bytes
-            // i.e. version, area length and language code
-            offset = 3;
-            fruAreaFieldNames = &productFruAreas;
-            break;
-        default:
-            std::cerr << "Don't know how to handle property " << propertyName
-                      << " \n";
-            return false;
-    }
-    if (fruAreaOffsetFieldValue == 0)
-    {
-        std::cerr << "FRU Area for " << propertyName << " not present \n";
+        std::cerr << "findFruAreaLocationAndField failed \n";
         return false;
     }
 
-    size_t fruAreaStart = fruAreaOffsetFieldValue * fruBlockSize;
-    size_t fruAreaSize = fruData[fruAreaStart + 1] * fruBlockSize;
-    size_t fruAreaEnd = fruAreaStart + fruAreaSize;
-    size_t fruDataIter = fruAreaStart + offset;
-    size_t skipToFRUUpdateField = 0;
     ssize_t fieldLength = 0;
 
-    bool found = false;
-    for (const auto& field : *fruAreaFieldNames)
-    {
-        skipToFRUUpdateField++;
-        if (propertyName == propertyNamePrefix + field)
-        {
-            found = true;
-            break;
-        }
-    }
-    if (!found)
-    {
-        std::size_t pos = propertyName.find(fruCustomFieldName);
-        if (pos == std::string::npos)
-        {
-            std::cerr << "PropertyName doesn't exist in FRU Area Vectors: "
-                      << propertyName << "\n";
-            return false;
-        }
-        std::string fieldNumStr =
-            propertyName.substr(pos + fruCustomFieldName.length());
-        size_t fieldNum = std::stoi(fieldNumStr);
-        if (fieldNum == 0)
-        {
-            std::cerr << "PropertyName not recognized: " << propertyName
-                      << "\n";
-            return false;
-        }
-        skipToFRUUpdateField += fieldNum;
-    }
-
-    for (size_t i = 1; i < skipToFRUUpdateField; i++)
-    {
-        fieldLength = getFieldLength(fruData[fruDataIter]);
-        if (fieldLength < 0)
-        {
-            break;
-        }
-        fruDataIter += 1 + fieldLength;
-    }
-    size_t fruUpdateFieldLoc = fruDataIter;
-
     // Push post update fru field bytes to a vector
-    fieldLength = getFieldLength(fruData[fruUpdateFieldLoc]);
+    fieldLength = getFieldLength(fruData[fruAreaParams.updateFieldLoc]);
     if (fieldLength < 0)
     {
         std::cerr << "Property " << propertyName << " not present \n";
@@ -1212,9 +1132,9 @@
     size_t endOfFieldsLoc = 0;
     while ((fieldLength = getFieldLength(fruData[fruDataIter])) >= 0)
     {
-        if (fruDataIter >= (fruAreaStart + fruAreaSize))
+        if (fruDataIter >= (fruAreaParams.start + fruAreaParams.size))
         {
-            fruDataIter = fruAreaStart + fruAreaSize;
+            fruDataIter = fruAreaParams.start + fruAreaParams.size;
             break;
         }
         fruDataIter += 1 + fieldLength;
@@ -1249,8 +1169,9 @@
 
     // check FRU area size
     size_t fruAreaDataSize =
-        ((fruUpdateFieldLoc - fruAreaStart + 1) + restFRUAreaFieldsData.size());
-    size_t fruAreaAvailableSize = fruAreaSize - fruAreaDataSize;
+        ((fruAreaParams.updateFieldLoc - fruAreaParams.start + 1) +
+         restFRUAreaFieldsData.size());
+    size_t fruAreaAvailableSize = fruAreaParams.size - fruAreaDataSize;
     if ((updatePropertyReqLen + 1) > fruAreaAvailableSize)
     {
 
@@ -1259,10 +1180,11 @@
         // round size to 8-byte blocks
         newFRUAreaSize =
             ((newFRUAreaSize - 1) / fruBlockSize + 1) * fruBlockSize;
-        size_t newFRUDataSize = fruData.size() + newFRUAreaSize - fruAreaSize;
+        size_t newFRUDataSize =
+            fruData.size() + newFRUAreaSize - fruAreaParams.size;
         fruData.resize(newFRUDataSize);
-        fruAreaSize = newFRUAreaSize;
-        fruAreaEnd = fruAreaStart + fruAreaSize;
+        fruAreaParams.size = newFRUAreaSize;
+        fruAreaParams.end = fruAreaParams.start + fruAreaParams.size;
 #else
         std::cerr << "FRU field length: " << updatePropertyReqLen + 1
                   << " should not be greater than available FRU area size: "
@@ -1273,21 +1195,21 @@
 
     // write new requested property field length and data
     constexpr uint8_t newTypeLenMask = 0xC0;
-    fruData[fruUpdateFieldLoc] =
+    fruData[fruAreaParams.updateFieldLoc] =
         static_cast<uint8_t>(updatePropertyReqLen | newTypeLenMask);
-    fruUpdateFieldLoc++;
+    fruAreaParams.updateFieldLoc++;
     std::copy(updatePropertyReq.begin(), updatePropertyReq.end(),
-              fruData.begin() + fruUpdateFieldLoc);
+              fruData.begin() + fruAreaParams.updateFieldLoc);
 
     // Copy remaining data to main fru area - post updated fru field vector
-    restFRUFieldsLoc = fruUpdateFieldLoc + updatePropertyReqLen;
+    restFRUFieldsLoc = fruAreaParams.updateFieldLoc + updatePropertyReqLen;
     size_t fruAreaDataEnd = restFRUFieldsLoc + restFRUAreaFieldsData.size();
     std::copy(restFRUAreaFieldsData.begin(), restFRUAreaFieldsData.end(),
               fruData.begin() + restFRUFieldsLoc);
 
     // Update final fru with new fru area length and checksum
     unsigned int nextFRUAreaNewLoc = updateFRUAreaLenAndChecksum(
-        fruData, fruAreaStart, fruAreaDataEnd, fruAreaEnd);
+        fruData, fruAreaParams.start, fruAreaDataEnd, fruAreaParams.end);
 
 #ifdef ENABLE_FRU_AREA_RESIZE
     ++nextFRUAreaNewLoc;
diff --git a/src/fru_utils.cpp b/src/fru_utils.cpp
index bc59af8..e030ac8 100644
--- a/src/fru_utils.cpp
+++ b/src/fru_utils.cpp
@@ -784,3 +784,111 @@
 
     return ret;
 }
+
+// Iterate FruArea Names and find start and size of the fru area that contains
+// the propertyName and the field start location for the property. fruAreaParams
+// struct values fruAreaStart, fruAreaSize, fruAreaEnd, fieldLoc values gets
+// updated/returned if successful.
+
+bool findFruAreaLocationAndField(std::vector<uint8_t>& fruData,
+                                 const std::string& propertyName,
+                                 struct FruArea& fruAreaParams,
+                                 size_t& fruDataIter)
+{
+    const std::vector<std::string>* fruAreaFieldNames = nullptr;
+
+    uint8_t fruAreaOffsetFieldValue = 0;
+    size_t offset = 0;
+    std::string areaName = propertyName.substr(0, propertyName.find('_'));
+    std::string propertyNamePrefix = areaName + "_";
+    auto it = std::find(fruAreaNames.begin(), fruAreaNames.end(), areaName);
+    if (it == fruAreaNames.end())
+    {
+        std::cerr << "Can't parse area name for property " << propertyName
+                  << " \n";
+        return false;
+    }
+    fruAreas fruAreaToUpdate = static_cast<fruAreas>(it - fruAreaNames.begin());
+    fruAreaOffsetFieldValue =
+        fruData[getHeaderAreaFieldOffset(fruAreaToUpdate)];
+    switch (fruAreaToUpdate)
+    {
+        case fruAreas::fruAreaChassis:
+            offset = 3; // chassis part number offset. Skip fixed first 3 bytes
+            fruAreaFieldNames = &chassisFruAreas;
+            break;
+        case fruAreas::fruAreaBoard:
+            offset = 6; // board manufacturer offset. Skip fixed first 6 bytes
+            fruAreaFieldNames = &boardFruAreas;
+            break;
+        case fruAreas::fruAreaProduct:
+            // Manufacturer name offset. Skip fixed first 3 product fru bytes
+            // i.e. version, area length and language code
+            offset = 3;
+            fruAreaFieldNames = &productFruAreas;
+            break;
+        default:
+            std::cerr << "Invalid PropertyName " << propertyName << " \n";
+            return false;
+    }
+    if (fruAreaOffsetFieldValue == 0)
+    {
+        std::cerr << "FRU Area for " << propertyName << " not present \n";
+        return false;
+    }
+
+    fruAreaParams.start = fruAreaOffsetFieldValue * fruBlockSize;
+    fruAreaParams.size = fruData[fruAreaParams.start + 1] * fruBlockSize;
+    fruAreaParams.end = fruAreaParams.start + fruAreaParams.size;
+    fruDataIter = fruAreaParams.start + offset;
+    size_t skipToFRUUpdateField = 0;
+    ssize_t fieldLength = 0;
+
+    bool found = false;
+    for (const auto& field : *fruAreaFieldNames)
+    {
+        skipToFRUUpdateField++;
+        if (propertyName == propertyNamePrefix + field)
+        {
+            found = true;
+            break;
+        }
+    }
+    if (!found)
+    {
+        std::size_t pos = propertyName.find(fruCustomFieldName);
+        if (pos == std::string::npos)
+        {
+            std::cerr << "PropertyName doesn't exist in FRU Area Vectors: "
+                      << propertyName << "\n";
+            return false;
+        }
+        std::string fieldNumStr =
+            propertyName.substr(pos + fruCustomFieldName.length());
+        size_t fieldNum = std::stoi(fieldNumStr);
+        if (fieldNum == 0)
+        {
+            std::cerr << "PropertyName not recognized: " << propertyName
+                      << "\n";
+            return false;
+        }
+        skipToFRUUpdateField += fieldNum;
+    }
+
+    for (size_t i = 1; i < skipToFRUUpdateField; i++)
+    {
+        if (fruDataIter < fruData.size())
+        {
+            fieldLength = getFieldLength(fruData[fruDataIter]);
+
+            if (fieldLength < 0)
+            {
+                break;
+            }
+            fruDataIter += 1 + fieldLength;
+        }
+    }
+    fruAreaParams.updateFieldLoc = fruDataIter;
+
+    return true;
+}