libpldmresponder: Implement GetFRURecordByOption

Implement handler for GetFRURecordByOption command

Signed-off-by: John Wang <wangzqbj@inspur.com>
Change-Id: If2bc90872b2521f1771aa800de6fbce569a5b339
diff --git a/libpldmresponder/fru.cpp b/libpldmresponder/fru.cpp
index ce470ab..cb7d5f7 100644
--- a/libpldmresponder/fru.cpp
+++ b/libpldmresponder/fru.cpp
@@ -204,6 +204,42 @@
                 iter);
 }
 
+int FruImpl::getFRURecordByOption(std::vector<uint8_t>& fruData,
+                                  uint16_t /* fruTableHandle */,
+                                  uint16_t recordSetIdentifer,
+                                  uint8_t recordType, uint8_t fieldType)
+{
+    // FRU table is built lazily, build if not done.
+    buildFRUTable();
+
+    /* 7 is sizeof(checksum,4) + padBytesMax(3)
+     * We can not know size of the record table got by options in advance, but
+     * it must be less than the source table. So it's safe to use sizeof the
+     * source table + 7 as the buffer length
+     */
+    size_t recordTableSize = table.size() - padBytes + 7;
+    fruData.resize(recordTableSize, 0);
+
+    get_fru_record_by_option(table.data(), table.size() - padBytes,
+                             fruData.data(), &recordTableSize,
+                             recordSetIdentifer, recordType, fieldType);
+
+    if (recordTableSize == 0)
+    {
+        return PLDM_FRU_DATA_STRUCTURE_TABLE_UNAVAILABLE;
+    }
+
+    auto pads = utils::getNumPadBytes(recordTableSize);
+    auto sum = crc32(fruData.data(), recordTableSize + pads);
+
+    auto iter = fruData.begin() + recordTableSize + pads;
+    std::copy_n(reinterpret_cast<const uint8_t*>(&checksum), sizeof(checksum),
+                iter);
+    fruData.resize(recordTableSize + pads + sizeof(sum));
+
+    return PLDM_SUCCESS;
+}
+
 namespace fru
 {
 
@@ -262,6 +298,57 @@
     return response;
 }
 
+Response Handler::getFRURecordByOption(const pldm_msg* request,
+                                       size_t payloadLength)
+{
+    if (payloadLength != sizeof(pldm_get_fru_record_by_option_req))
+    {
+        return ccOnlyResponse(request, PLDM_ERROR_INVALID_LENGTH);
+    }
+
+    uint32_t retDataTransferHandle{};
+    uint16_t retFruTableHandle{};
+    uint16_t retRecordSetIdentifier{};
+    uint8_t retRecordType{};
+    uint8_t retFieldType{};
+    uint8_t retTransferOpFlag{};
+
+    auto rc = decode_get_fru_record_by_option_req(
+        request, payloadLength, &retDataTransferHandle, &retFruTableHandle,
+        &retRecordSetIdentifier, &retRecordType, &retFieldType,
+        &retTransferOpFlag);
+
+    if (rc != PLDM_SUCCESS)
+    {
+        return ccOnlyResponse(request, rc);
+    }
+
+    std::vector<uint8_t> fruData;
+    rc = impl.getFRURecordByOption(fruData, retFruTableHandle,
+                                   retRecordSetIdentifier, retRecordType,
+                                   retFieldType);
+    if (rc != PLDM_SUCCESS)
+    {
+        return ccOnlyResponse(request, rc);
+    }
+
+    auto respPayloadLength =
+        PLDM_GET_FRU_RECORD_BY_OPTION_MIN_RESP_BYTES + fruData.size();
+    Response response(sizeof(pldm_msg_hdr) + respPayloadLength, 0);
+    auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+
+    rc = encode_get_fru_record_by_option_resp(
+        request->hdr.instance_id, PLDM_SUCCESS, 0, PLDM_START_AND_END,
+        fruData.data(), fruData.size(), responsePtr, respPayloadLength);
+
+    if (rc != PLDM_SUCCESS)
+    {
+        return ccOnlyResponse(request, rc);
+    }
+
+    return response;
+}
+
 } // namespace fru
 
 } // namespace responder