1f7f5da97SDeepak Kodihalli #include "pldm_fru_cmd.hpp" 2f7f5da97SDeepak Kodihalli 3f7f5da97SDeepak Kodihalli #include "pldm_cmd_helper.hpp" 4f7f5da97SDeepak Kodihalli 5*5bdde3a4SJohn Wang #include <endian.h> 6*5bdde3a4SJohn Wang 7*5bdde3a4SJohn Wang #include <functional> 8*5bdde3a4SJohn Wang #include <tuple> 9*5bdde3a4SJohn Wang 10f7f5da97SDeepak Kodihalli namespace pldmtool 11f7f5da97SDeepak Kodihalli { 12f7f5da97SDeepak Kodihalli 13f7f5da97SDeepak Kodihalli namespace fru 14f7f5da97SDeepak Kodihalli { 15f7f5da97SDeepak Kodihalli 16f7f5da97SDeepak Kodihalli namespace 17f7f5da97SDeepak Kodihalli { 18f7f5da97SDeepak Kodihalli 19f7f5da97SDeepak Kodihalli using namespace pldmtool::helper; 20f7f5da97SDeepak Kodihalli 21f7f5da97SDeepak Kodihalli std::vector<std::unique_ptr<CommandInterface>> commands; 22f7f5da97SDeepak Kodihalli 23f7f5da97SDeepak Kodihalli } // namespace 24f7f5da97SDeepak Kodihalli 25f7f5da97SDeepak Kodihalli class GetFruRecordTableMetadata : public CommandInterface 26f7f5da97SDeepak Kodihalli { 27f7f5da97SDeepak Kodihalli public: 28f7f5da97SDeepak Kodihalli ~GetFruRecordTableMetadata() = default; 29f7f5da97SDeepak Kodihalli GetFruRecordTableMetadata() = delete; 30f7f5da97SDeepak Kodihalli GetFruRecordTableMetadata(const GetFruRecordTableMetadata&) = delete; 31f7f5da97SDeepak Kodihalli GetFruRecordTableMetadata(GetFruRecordTableMetadata&&) = default; 32f7f5da97SDeepak Kodihalli GetFruRecordTableMetadata& 33f7f5da97SDeepak Kodihalli operator=(const GetFruRecordTableMetadata&) = delete; 34f7f5da97SDeepak Kodihalli GetFruRecordTableMetadata& operator=(GetFruRecordTableMetadata&&) = default; 35f7f5da97SDeepak Kodihalli 36f7f5da97SDeepak Kodihalli using CommandInterface::CommandInterface; 37f7f5da97SDeepak Kodihalli 38f7f5da97SDeepak Kodihalli std::pair<int, std::vector<uint8_t>> createRequestMsg() override 39f7f5da97SDeepak Kodihalli { 40f7f5da97SDeepak Kodihalli std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr)); 41f7f5da97SDeepak Kodihalli auto request = reinterpret_cast<pldm_msg*>(requestMsg.data()); 42f7f5da97SDeepak Kodihalli 43f7f5da97SDeepak Kodihalli auto rc = encode_get_fru_record_table_metadata_req( 44f7f5da97SDeepak Kodihalli instanceId, request, PLDM_GET_FRU_RECORD_TABLE_METADATA_REQ_BYTES); 45f7f5da97SDeepak Kodihalli return {rc, requestMsg}; 46f7f5da97SDeepak Kodihalli } 47f7f5da97SDeepak Kodihalli 48f7f5da97SDeepak Kodihalli void parseResponseMsg(pldm_msg* responsePtr, size_t payloadLength) override 49f7f5da97SDeepak Kodihalli { 50f7f5da97SDeepak Kodihalli uint8_t cc = 0; 51f7f5da97SDeepak Kodihalli uint8_t fru_data_major_version, fru_data_minor_version; 52f7f5da97SDeepak Kodihalli uint32_t fru_table_maximum_size, fru_table_length; 53f7f5da97SDeepak Kodihalli uint16_t total_record_set_identifiers, total_table_records; 54f7f5da97SDeepak Kodihalli uint32_t checksum; 55f7f5da97SDeepak Kodihalli 56f7f5da97SDeepak Kodihalli auto rc = decode_get_fru_record_table_metadata_resp( 57f7f5da97SDeepak Kodihalli responsePtr, payloadLength, &cc, &fru_data_major_version, 58f7f5da97SDeepak Kodihalli &fru_data_minor_version, &fru_table_maximum_size, &fru_table_length, 59f7f5da97SDeepak Kodihalli &total_record_set_identifiers, &total_table_records, &checksum); 60f7f5da97SDeepak Kodihalli if (rc != PLDM_SUCCESS || cc != PLDM_SUCCESS) 61f7f5da97SDeepak Kodihalli { 62f7f5da97SDeepak Kodihalli std::cerr << "Response Message Error: " 63f7f5da97SDeepak Kodihalli << "rc=" << rc << ",cc=" << (int)cc << std::endl; 64f7f5da97SDeepak Kodihalli return; 65f7f5da97SDeepak Kodihalli } 66f7f5da97SDeepak Kodihalli std::cout << "FRUDATAMajorVersion : " 67f7f5da97SDeepak Kodihalli << static_cast<uint32_t>(fru_data_major_version) << std::endl; 68f7f5da97SDeepak Kodihalli std::cout << "FRUDATAMinorVersion : " 69f7f5da97SDeepak Kodihalli << static_cast<uint32_t>(fru_data_minor_version) << std::endl; 70f7f5da97SDeepak Kodihalli std::cout << "FRUTableMaximumSize : " << fru_table_maximum_size 71f7f5da97SDeepak Kodihalli << std::endl; 72f7f5da97SDeepak Kodihalli std::cout << "FRUTableLength : " << fru_table_length << std::endl; 73f7f5da97SDeepak Kodihalli std::cout << "Total number of Record Set Identifiers in table : " 74f7f5da97SDeepak Kodihalli << total_record_set_identifiers << std::endl; 75f7f5da97SDeepak Kodihalli std::cout << "Total number of records in table : " 76f7f5da97SDeepak Kodihalli << total_table_records << std::endl; 77f7f5da97SDeepak Kodihalli std::cout << "FRU DATAStructureTableIntegrityChecksum : " << checksum 78f7f5da97SDeepak Kodihalli << std::endl; 79f7f5da97SDeepak Kodihalli } 80f7f5da97SDeepak Kodihalli }; 81f7f5da97SDeepak Kodihalli 82*5bdde3a4SJohn Wang class FRUTablePrint 83*5bdde3a4SJohn Wang { 84*5bdde3a4SJohn Wang public: 85*5bdde3a4SJohn Wang explicit FRUTablePrint(const uint8_t* table, size_t table_size) : 86*5bdde3a4SJohn Wang table(table), table_size(table_size) 87*5bdde3a4SJohn Wang {} 88*5bdde3a4SJohn Wang 89*5bdde3a4SJohn Wang void print() 90*5bdde3a4SJohn Wang { 91*5bdde3a4SJohn Wang auto p = table; 92*5bdde3a4SJohn Wang while (!isTableEnd(p)) 93*5bdde3a4SJohn Wang { 94*5bdde3a4SJohn Wang auto record = 95*5bdde3a4SJohn Wang reinterpret_cast<const pldm_fru_record_data_format*>(p); 96*5bdde3a4SJohn Wang std::cout << "FRU Record Set Identifier: " 97*5bdde3a4SJohn Wang << (int)le16toh(record->record_set_id) << std::endl; 98*5bdde3a4SJohn Wang std::cout << "FRU Record Type: " 99*5bdde3a4SJohn Wang << typeToString(fruRecordTypes, record->record_type) 100*5bdde3a4SJohn Wang << std::endl; 101*5bdde3a4SJohn Wang std::cout << "Number of FRU fields: " << (int)record->num_fru_fields 102*5bdde3a4SJohn Wang << std::endl; 103*5bdde3a4SJohn Wang std::cout << "Encoding Type for FRU fields: " 104*5bdde3a4SJohn Wang << typeToString(fruEncodingType, record->encoding_type) 105*5bdde3a4SJohn Wang << std::endl; 106*5bdde3a4SJohn Wang 107*5bdde3a4SJohn Wang auto isGeneralRec = true; 108*5bdde3a4SJohn Wang if (record->record_type != PLDM_FRU_RECORD_TYPE_GENERAL) 109*5bdde3a4SJohn Wang { 110*5bdde3a4SJohn Wang isGeneralRec = false; 111*5bdde3a4SJohn Wang } 112*5bdde3a4SJohn Wang 113*5bdde3a4SJohn Wang p += sizeof(pldm_fru_record_data_format) - 114*5bdde3a4SJohn Wang sizeof(pldm_fru_record_tlv); 115*5bdde3a4SJohn Wang for (int i = 0; i < record->num_fru_fields; i++) 116*5bdde3a4SJohn Wang { 117*5bdde3a4SJohn Wang auto tlv = reinterpret_cast<const pldm_fru_record_tlv*>(p); 118*5bdde3a4SJohn Wang if (isGeneralRec) 119*5bdde3a4SJohn Wang { 120*5bdde3a4SJohn Wang fruFieldPrint(record->record_type, tlv->type, tlv->length, 121*5bdde3a4SJohn Wang tlv->value); 122*5bdde3a4SJohn Wang } 123*5bdde3a4SJohn Wang p += sizeof(pldm_fru_record_tlv) - 1 + tlv->length; 124*5bdde3a4SJohn Wang } 125*5bdde3a4SJohn Wang } 126*5bdde3a4SJohn Wang } 127*5bdde3a4SJohn Wang 128*5bdde3a4SJohn Wang private: 129*5bdde3a4SJohn Wang const uint8_t* table; 130*5bdde3a4SJohn Wang size_t table_size; 131*5bdde3a4SJohn Wang 132*5bdde3a4SJohn Wang bool isTableEnd(const uint8_t* p) 133*5bdde3a4SJohn Wang { 134*5bdde3a4SJohn Wang auto offset = p - table; 135*5bdde3a4SJohn Wang return (table_size - offset) <= 7; 136*5bdde3a4SJohn Wang } 137*5bdde3a4SJohn Wang 138*5bdde3a4SJohn Wang static inline const std::map<uint8_t, const char*> fruEncodingType{ 139*5bdde3a4SJohn Wang {PLDM_FRU_ENCODING_UNSPECIFIED, "Unspecified"}, 140*5bdde3a4SJohn Wang {PLDM_FRU_ENCODING_ASCII, "ASCII"}, 141*5bdde3a4SJohn Wang {PLDM_FRU_ENCODING_UTF8, "UTF8"}, 142*5bdde3a4SJohn Wang {PLDM_FRU_ENCODING_UTF16, "UTF16"}, 143*5bdde3a4SJohn Wang {PLDM_FRU_ENCODING_UTF16LE, "UTF16LE"}, 144*5bdde3a4SJohn Wang {PLDM_FRU_ENCODING_UTF16BE, "UTF16BE"}}; 145*5bdde3a4SJohn Wang 146*5bdde3a4SJohn Wang static inline const std::map<uint8_t, const char*> fruRecordTypes{ 147*5bdde3a4SJohn Wang {PLDM_FRU_RECORD_TYPE_GENERAL, "General"}, 148*5bdde3a4SJohn Wang {PLDM_FRU_RECORD_TYPE_OEM, "OEM"}}; 149*5bdde3a4SJohn Wang 150*5bdde3a4SJohn Wang std::string typeToString(std::map<uint8_t, const char*> typeMap, 151*5bdde3a4SJohn Wang uint8_t type) 152*5bdde3a4SJohn Wang { 153*5bdde3a4SJohn Wang auto typeString = std::to_string(type); 154*5bdde3a4SJohn Wang try 155*5bdde3a4SJohn Wang { 156*5bdde3a4SJohn Wang return typeString + "(" + typeMap.at(type) + ")"; 157*5bdde3a4SJohn Wang } 158*5bdde3a4SJohn Wang catch (const std::out_of_range& e) 159*5bdde3a4SJohn Wang { 160*5bdde3a4SJohn Wang return typeString; 161*5bdde3a4SJohn Wang } 162*5bdde3a4SJohn Wang } 163*5bdde3a4SJohn Wang 164*5bdde3a4SJohn Wang using FruFieldParser = 165*5bdde3a4SJohn Wang std::function<std::string(const uint8_t* value, uint8_t length)>; 166*5bdde3a4SJohn Wang 167*5bdde3a4SJohn Wang using FieldType = uint8_t; 168*5bdde3a4SJohn Wang using RecordType = uint8_t; 169*5bdde3a4SJohn Wang using FieldName = std::string; 170*5bdde3a4SJohn Wang using FruFieldTypes = 171*5bdde3a4SJohn Wang std::map<FieldType, std::tuple<FieldName, FruFieldParser>>; 172*5bdde3a4SJohn Wang 173*5bdde3a4SJohn Wang static std::string fruFieldParserString(const uint8_t* value, 174*5bdde3a4SJohn Wang uint8_t length) 175*5bdde3a4SJohn Wang { 176*5bdde3a4SJohn Wang return std::string(reinterpret_cast<const char*>(value), length); 177*5bdde3a4SJohn Wang } 178*5bdde3a4SJohn Wang 179*5bdde3a4SJohn Wang static std::string fruFieldParserTimestamp(const uint8_t*, uint8_t) 180*5bdde3a4SJohn Wang { 181*5bdde3a4SJohn Wang return std::string("TODO"); 182*5bdde3a4SJohn Wang } 183*5bdde3a4SJohn Wang 184*5bdde3a4SJohn Wang static std::string fruFieldParserU32(const uint8_t* value, uint8_t length) 185*5bdde3a4SJohn Wang { 186*5bdde3a4SJohn Wang assert(length == 4); 187*5bdde3a4SJohn Wang uint32_t v; 188*5bdde3a4SJohn Wang std::memcpy(&v, value, length); 189*5bdde3a4SJohn Wang return std::to_string(le32toh(v)); 190*5bdde3a4SJohn Wang } 191*5bdde3a4SJohn Wang 192*5bdde3a4SJohn Wang static inline const FruFieldTypes fruGeneralFieldTypes = { 193*5bdde3a4SJohn Wang {PLDM_FRU_FIELD_TYPE_CHASSIS, {"Chassis", fruFieldParserString}}, 194*5bdde3a4SJohn Wang {PLDM_FRU_FIELD_TYPE_MODEL, {"Model", fruFieldParserString}}, 195*5bdde3a4SJohn Wang {PLDM_FRU_FIELD_TYPE_PN, {"Part Number", fruFieldParserString}}, 196*5bdde3a4SJohn Wang {PLDM_FRU_FIELD_TYPE_SN, {"Serial Number", fruFieldParserString}}, 197*5bdde3a4SJohn Wang {PLDM_FRU_FIELD_TYPE_MANUFAC, {"Manufacturer", fruFieldParserString}}, 198*5bdde3a4SJohn Wang {PLDM_FRU_FIELD_TYPE_MANUFAC_DATE, 199*5bdde3a4SJohn Wang {"Manufacture Date", fruFieldParserTimestamp}}, 200*5bdde3a4SJohn Wang {PLDM_FRU_FIELD_TYPE_VENDOR, {"Vendor", fruFieldParserString}}, 201*5bdde3a4SJohn Wang {PLDM_FRU_FIELD_TYPE_NAME, {"Name", fruFieldParserString}}, 202*5bdde3a4SJohn Wang {PLDM_FRU_FIELD_TYPE_SKU, {"SKU", fruFieldParserString}}, 203*5bdde3a4SJohn Wang {PLDM_FRU_FIELD_TYPE_VERSION, {"Version", fruFieldParserString}}, 204*5bdde3a4SJohn Wang {PLDM_FRU_FIELD_TYPE_ASSET_TAG, {"Asset Tag", fruFieldParserString}}, 205*5bdde3a4SJohn Wang {PLDM_FRU_FIELD_TYPE_DESC, {"Description", fruFieldParserString}}, 206*5bdde3a4SJohn Wang {PLDM_FRU_FIELD_TYPE_EC_LVL, 207*5bdde3a4SJohn Wang {"Engineering Change Level", fruFieldParserString}}, 208*5bdde3a4SJohn Wang {PLDM_FRU_FIELD_TYPE_OTHER, 209*5bdde3a4SJohn Wang {"Other Information", fruFieldParserString}}, 210*5bdde3a4SJohn Wang {PLDM_FRU_FIELD_TYPE_IANA, {"Vendor IANA", fruFieldParserU32}}, 211*5bdde3a4SJohn Wang }; 212*5bdde3a4SJohn Wang 213*5bdde3a4SJohn Wang static inline const FruFieldTypes fruOEMFieldTypes = { 214*5bdde3a4SJohn Wang {1, {"Vendor IANA", fruFieldParserU32}}, 215*5bdde3a4SJohn Wang 216*5bdde3a4SJohn Wang }; 217*5bdde3a4SJohn Wang 218*5bdde3a4SJohn Wang static inline const std::map<RecordType, FruFieldTypes> fruFieldTypes{ 219*5bdde3a4SJohn Wang {PLDM_FRU_RECORD_TYPE_GENERAL, fruGeneralFieldTypes}, 220*5bdde3a4SJohn Wang {PLDM_FRU_RECORD_TYPE_OEM, fruOEMFieldTypes}}; 221*5bdde3a4SJohn Wang 222*5bdde3a4SJohn Wang void fruFieldPrint(uint8_t recordType, uint8_t type, uint8_t length, 223*5bdde3a4SJohn Wang const uint8_t* value) 224*5bdde3a4SJohn Wang { 225*5bdde3a4SJohn Wang auto& [typeString, parser] = fruFieldTypes.at(recordType).at(type); 226*5bdde3a4SJohn Wang 227*5bdde3a4SJohn Wang std::cout << "\tFRU Field Type: " << typeString << std::endl; 228*5bdde3a4SJohn Wang std::cout << "\tFRU Field Length: " << (int)(length) << std::endl; 229*5bdde3a4SJohn Wang std::cout << "\tFRU Field Value: " << parser(value, length) 230*5bdde3a4SJohn Wang << std::endl; 231*5bdde3a4SJohn Wang } 232*5bdde3a4SJohn Wang }; 233*5bdde3a4SJohn Wang 234*5bdde3a4SJohn Wang class GetFRURecordByOption : public CommandInterface 235*5bdde3a4SJohn Wang { 236*5bdde3a4SJohn Wang public: 237*5bdde3a4SJohn Wang ~GetFRURecordByOption() = default; 238*5bdde3a4SJohn Wang GetFRURecordByOption() = delete; 239*5bdde3a4SJohn Wang GetFRURecordByOption(const GetFRURecordByOption&) = delete; 240*5bdde3a4SJohn Wang GetFRURecordByOption(GetFruRecordTableMetadata&&) = delete; 241*5bdde3a4SJohn Wang GetFRURecordByOption& operator=(const GetFRURecordByOption&) = delete; 242*5bdde3a4SJohn Wang GetFRURecordByOption& operator=(GetFRURecordByOption&&) = delete; 243*5bdde3a4SJohn Wang 244*5bdde3a4SJohn Wang explicit GetFRURecordByOption(const char* type, const char* name, 245*5bdde3a4SJohn Wang CLI::App* app) : 246*5bdde3a4SJohn Wang CommandInterface(type, name, app) 247*5bdde3a4SJohn Wang { 248*5bdde3a4SJohn Wang app->add_option("-i, --identifier", recordSetIdentifier, 249*5bdde3a4SJohn Wang "Record Set Identifier\n" 250*5bdde3a4SJohn Wang "Possible values: {All record sets = 0, Specific " 251*5bdde3a4SJohn Wang "record set = 1 – 65535}") 252*5bdde3a4SJohn Wang ->required(); 253*5bdde3a4SJohn Wang app->add_option("-r, --record", recordType, 254*5bdde3a4SJohn Wang "Record Type\n" 255*5bdde3a4SJohn Wang "Possible values: {All record types = 0, Specific " 256*5bdde3a4SJohn Wang "record types = 1 – 255}") 257*5bdde3a4SJohn Wang ->required(); 258*5bdde3a4SJohn Wang app->add_option("-f, --field", fieldType, 259*5bdde3a4SJohn Wang "Field Type\n" 260*5bdde3a4SJohn Wang "Possible values: {All record field types = 0, " 261*5bdde3a4SJohn Wang "Specific field types = 1 – 15}") 262*5bdde3a4SJohn Wang ->required(); 263*5bdde3a4SJohn Wang } 264*5bdde3a4SJohn Wang 265*5bdde3a4SJohn Wang std::pair<int, std::vector<uint8_t>> createRequestMsg() override 266*5bdde3a4SJohn Wang { 267*5bdde3a4SJohn Wang if (fieldType != 0 && recordType == 0) 268*5bdde3a4SJohn Wang { 269*5bdde3a4SJohn Wang throw std::invalid_argument("if field type is non-zero, the record " 270*5bdde3a4SJohn Wang "type shall also be non-zero"); 271*5bdde3a4SJohn Wang } 272*5bdde3a4SJohn Wang 273*5bdde3a4SJohn Wang auto payloadLength = sizeof(pldm_get_fru_record_by_option_req); 274*5bdde3a4SJohn Wang 275*5bdde3a4SJohn Wang std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) + payloadLength, 276*5bdde3a4SJohn Wang 0); 277*5bdde3a4SJohn Wang auto reqMsg = reinterpret_cast<pldm_msg*>(requestMsg.data()); 278*5bdde3a4SJohn Wang 279*5bdde3a4SJohn Wang auto rc = encode_get_fru_record_by_option_req( 280*5bdde3a4SJohn Wang instanceId, 0 /* DataTransferHandle */, 0 /* FRUTableHandle */, 281*5bdde3a4SJohn Wang recordSetIdentifier, recordType, fieldType, PLDM_GET_FIRSTPART, 282*5bdde3a4SJohn Wang reqMsg, payloadLength); 283*5bdde3a4SJohn Wang 284*5bdde3a4SJohn Wang return {rc, requestMsg}; 285*5bdde3a4SJohn Wang } 286*5bdde3a4SJohn Wang 287*5bdde3a4SJohn Wang void parseResponseMsg(pldm_msg* responsePtr, size_t payloadLength) override 288*5bdde3a4SJohn Wang { 289*5bdde3a4SJohn Wang uint8_t cc; 290*5bdde3a4SJohn Wang uint32_t dataTransferHandle; 291*5bdde3a4SJohn Wang uint8_t transferFlag; 292*5bdde3a4SJohn Wang variable_field fruData; 293*5bdde3a4SJohn Wang 294*5bdde3a4SJohn Wang auto rc = decode_get_fru_record_by_option_resp( 295*5bdde3a4SJohn Wang responsePtr, payloadLength, &cc, &dataTransferHandle, &transferFlag, 296*5bdde3a4SJohn Wang &fruData); 297*5bdde3a4SJohn Wang 298*5bdde3a4SJohn Wang if (rc != PLDM_SUCCESS || cc != PLDM_SUCCESS) 299*5bdde3a4SJohn Wang { 300*5bdde3a4SJohn Wang std::cerr << "Response Message Error: " 301*5bdde3a4SJohn Wang << "rc=" << rc << ",cc=" << (int)cc << std::endl; 302*5bdde3a4SJohn Wang return; 303*5bdde3a4SJohn Wang } 304*5bdde3a4SJohn Wang 305*5bdde3a4SJohn Wang FRUTablePrint tablePrint(fruData.ptr, fruData.length); 306*5bdde3a4SJohn Wang tablePrint.print(); 307*5bdde3a4SJohn Wang } 308*5bdde3a4SJohn Wang 309*5bdde3a4SJohn Wang private: 310*5bdde3a4SJohn Wang uint16_t recordSetIdentifier; 311*5bdde3a4SJohn Wang uint8_t recordType; 312*5bdde3a4SJohn Wang uint8_t fieldType; 313*5bdde3a4SJohn Wang }; 314*5bdde3a4SJohn Wang 315f7f5da97SDeepak Kodihalli void registerCommand(CLI::App& app) 316f7f5da97SDeepak Kodihalli { 317f7f5da97SDeepak Kodihalli auto fru = app.add_subcommand("fru", "FRU type command"); 318f7f5da97SDeepak Kodihalli fru->require_subcommand(1); 319f7f5da97SDeepak Kodihalli auto getFruRecordTableMetadata = fru->add_subcommand( 320f7f5da97SDeepak Kodihalli "GetFruRecordTableMetadata", "get FRU record table metadata"); 321f7f5da97SDeepak Kodihalli commands.push_back(std::make_unique<GetFruRecordTableMetadata>( 322f7f5da97SDeepak Kodihalli "fru", "GetFruRecordTableMetadata", getFruRecordTableMetadata)); 323*5bdde3a4SJohn Wang 324*5bdde3a4SJohn Wang auto getFRURecordByOption = 325*5bdde3a4SJohn Wang fru->add_subcommand("GetFRURecordByOption", "get FRU Record By Option"); 326*5bdde3a4SJohn Wang commands.push_back(std::make_unique<GetFRURecordByOption>( 327*5bdde3a4SJohn Wang "fru", "GetFRURecordByOption", getFRURecordByOption)); 328f7f5da97SDeepak Kodihalli } 329f7f5da97SDeepak Kodihalli 330f7f5da97SDeepak Kodihalli } // namespace fru 331f7f5da97SDeepak Kodihalli 332f7f5da97SDeepak Kodihalli } // namespace pldmtool 333