1 #include "terminus_manager.hpp"
2 
3 #include "manager.hpp"
4 
5 #include <phosphor-logging/lg2.hpp>
6 
7 PHOSPHOR_LOG2_USING;
8 
9 namespace pldm
10 {
11 namespace platform_mc
12 {
13 
toMctpInfo(const pldm_tid_t & tid)14 std::optional<MctpInfo> TerminusManager::toMctpInfo(const pldm_tid_t& tid)
15 {
16     if (tid == PLDM_TID_UNASSIGNED || tid == PLDM_TID_RESERVED)
17     {
18         return std::nullopt;
19     }
20 
21     if ((!this->transportLayerTable.contains(tid)) ||
22         (this->transportLayerTable[tid] != SupportedTransportLayer::MCTP))
23     {
24         return std::nullopt;
25     }
26 
27     auto mctpInfoIt = mctpInfoTable.find(tid);
28     if (mctpInfoIt == mctpInfoTable.end())
29     {
30         return std::nullopt;
31     }
32 
33     return mctpInfoIt->second;
34 }
35 
toTid(const MctpInfo & mctpInfo) const36 std::optional<pldm_tid_t> TerminusManager::toTid(const MctpInfo& mctpInfo) const
37 {
38     if (!pldm::utils::isValidEID(std::get<0>(mctpInfo)))
39     {
40         return std::nullopt;
41     }
42 
43     auto mctpInfoTableIt = std::find_if(
44         mctpInfoTable.begin(), mctpInfoTable.end(), [&mctpInfo](auto& v) {
45             return (std::get<0>(v.second) == std::get<0>(mctpInfo)) &&
46                    (std::get<3>(v.second) == std::get<3>(mctpInfo));
47         });
48     if (mctpInfoTableIt == mctpInfoTable.end())
49     {
50         return std::nullopt;
51     }
52     return mctpInfoTableIt->first;
53 }
54 
55 std::optional<pldm_tid_t>
storeTerminusInfo(const MctpInfo & mctpInfo,pldm_tid_t tid)56     TerminusManager::storeTerminusInfo(const MctpInfo& mctpInfo, pldm_tid_t tid)
57 {
58     if (tid == PLDM_TID_UNASSIGNED || tid == PLDM_TID_RESERVED)
59     {
60         return std::nullopt;
61     }
62 
63     if (!pldm::utils::isValidEID(std::get<0>(mctpInfo)))
64     {
65         return std::nullopt;
66     }
67 
68     if (tidPool[tid])
69     {
70         return std::nullopt;
71     }
72 
73     tidPool[tid] = true;
74     transportLayerTable[tid] = SupportedTransportLayer::MCTP;
75     mctpInfoTable[tid] = mctpInfo;
76 
77     return tid;
78 }
79 
mapTid(const MctpInfo & mctpInfo)80 std::optional<pldm_tid_t> TerminusManager::mapTid(const MctpInfo& mctpInfo)
81 {
82     if (!pldm::utils::isValidEID(std::get<0>(mctpInfo)))
83     {
84         return std::nullopt;
85     }
86 
87     auto mctpInfoTableIt = std::find_if(
88         mctpInfoTable.begin(), mctpInfoTable.end(), [&mctpInfo](auto& v) {
89             return (std::get<0>(v.second) == std::get<0>(mctpInfo)) &&
90                    (std::get<3>(v.second) == std::get<3>(mctpInfo));
91         });
92     if (mctpInfoTableIt != mctpInfoTable.end())
93     {
94         return mctpInfoTableIt->first;
95     }
96 
97     auto tidPoolIt = std::find(tidPool.begin(), tidPool.end(), false);
98     if (tidPoolIt == tidPool.end())
99     {
100         return std::nullopt;
101     }
102 
103     pldm_tid_t tid = std::distance(tidPool.begin(), tidPoolIt);
104     return storeTerminusInfo(mctpInfo, tid);
105 }
106 
unmapTid(const pldm_tid_t & tid)107 bool TerminusManager::unmapTid(const pldm_tid_t& tid)
108 {
109     if (tid == PLDM_TID_UNASSIGNED || tid == PLDM_TID_RESERVED)
110     {
111         return false;
112     }
113     tidPool[tid] = false;
114 
115     if (transportLayerTable.contains(tid))
116     {
117         transportLayerTable.erase(tid);
118     }
119 
120     if (mctpInfoTable.contains(tid))
121     {
122         mctpInfoTable.erase(tid);
123     }
124 
125     return true;
126 }
127 
discoverMctpTerminus(const MctpInfos & mctpInfos)128 void TerminusManager::discoverMctpTerminus(const MctpInfos& mctpInfos)
129 {
130     queuedMctpInfos.emplace(mctpInfos);
131     if (discoverMctpTerminusTaskHandle.has_value())
132     {
133         auto& [scope, rcOpt] = *discoverMctpTerminusTaskHandle;
134         if (!rcOpt.has_value())
135         {
136             return;
137         }
138         stdexec::sync_wait(scope.on_empty());
139         discoverMctpTerminusTaskHandle.reset();
140     }
141     auto& [scope, rcOpt] = discoverMctpTerminusTaskHandle.emplace();
142     scope.spawn(discoverMctpTerminusTask() |
143                     stdexec::then([&](int rc) { rcOpt.emplace(rc); }),
144                 exec::default_task_context<void>(exec::inline_scheduler{}));
145 }
146 
147 TerminiMapper::iterator
findTerminusPtr(const MctpInfo & mctpInfo)148     TerminusManager::findTerminusPtr(const MctpInfo& mctpInfo)
149 {
150     auto foundIter = std::find_if(
151         termini.begin(), termini.end(), [&](const auto& terminusPair) {
152             auto terminusMctpInfo = toMctpInfo(terminusPair.first);
153             return (terminusMctpInfo &&
154                     (std::get<0>(terminusMctpInfo.value()) ==
155                      std::get<0>(mctpInfo)) &&
156                     (std::get<3>(terminusMctpInfo.value()) ==
157                      std::get<3>(mctpInfo)));
158         });
159 
160     return foundIter;
161 }
162 
discoverMctpTerminusTask()163 exec::task<int> TerminusManager::discoverMctpTerminusTask()
164 {
165     std::vector<pldm_tid_t> addedTids;
166     while (!queuedMctpInfos.empty())
167     {
168         if (manager)
169         {
170             co_await manager->beforeDiscoverTerminus();
171         }
172 
173         const MctpInfos& mctpInfos = queuedMctpInfos.front();
174         for (const auto& mctpInfo : mctpInfos)
175         {
176             auto it = findTerminusPtr(mctpInfo);
177             if (it == termini.end())
178             {
179                 co_await initMctpTerminus(mctpInfo);
180             }
181 
182             /* Get TID of initialized terminus */
183             auto tid = toTid(mctpInfo);
184             if (!tid)
185             {
186                 co_return PLDM_ERROR;
187             }
188             addedTids.push_back(tid.value());
189         }
190 
191         if (manager)
192         {
193             co_await manager->afterDiscoverTerminus();
194             for (const auto& tid : addedTids)
195             {
196                 manager->startSensorPolling(tid);
197             }
198         }
199 
200         queuedMctpInfos.pop();
201     }
202 
203     co_return PLDM_SUCCESS;
204 }
205 
removeMctpTerminus(const MctpInfos & mctpInfos)206 void TerminusManager::removeMctpTerminus(const MctpInfos& mctpInfos)
207 {
208     // remove terminus
209     for (const auto& mctpInfo : mctpInfos)
210     {
211         auto it = findTerminusPtr(mctpInfo);
212         if (it == termini.end())
213         {
214             continue;
215         }
216 
217         if (manager)
218         {
219             manager->stopSensorPolling(it->second->getTid());
220         }
221 
222         unmapTid(it->first);
223         termini.erase(it);
224     }
225 }
226 
initMctpTerminus(const MctpInfo & mctpInfo)227 exec::task<int> TerminusManager::initMctpTerminus(const MctpInfo& mctpInfo)
228 {
229     mctp_eid_t eid = std::get<0>(mctpInfo);
230     pldm_tid_t tid = 0;
231     bool isMapped = false;
232     auto rc = co_await getTidOverMctp(eid, &tid);
233     if (rc != PLDM_SUCCESS)
234     {
235         lg2::error("Failed to Get Terminus ID, error {ERROR}.", "ERROR", rc);
236         co_return PLDM_ERROR;
237     }
238 
239     if (tid == PLDM_TID_RESERVED)
240     {
241         lg2::error("Terminus responses the reserved {TID}.", "TID", tid);
242         co_return PLDM_ERROR;
243     }
244 
245     /* Terminus already has TID */
246     if (tid != PLDM_TID_UNASSIGNED)
247     {
248         /* TID is used by one discovered terminus */
249         auto it = termini.find(tid);
250         if (it != termini.end())
251         {
252             auto terminusMctpInfo = toMctpInfo(it->first);
253             /* The discovered terminus has the same MCTP Info */
254             if (terminusMctpInfo &&
255                 (std::get<0>(terminusMctpInfo.value()) ==
256                  std::get<0>(mctpInfo)) &&
257                 (std::get<3>(terminusMctpInfo.value()) ==
258                  std::get<3>(mctpInfo)))
259             {
260                 co_return PLDM_SUCCESS;
261             }
262             else
263             {
264                 /* ToDo:
265                  * Maybe the terminus supports multiple medium interfaces
266                  * Or the TID is used by other terminus.
267                  * Check the UUID to confirm.
268                  */
269                 isMapped = false;
270             }
271         }
272         /* Use the terminus TID for mapping */
273         else
274         {
275             auto mappedTid = storeTerminusInfo(mctpInfo, tid);
276             if (!mappedTid)
277             {
278                 lg2::error("Failed to store Terminus Info for terminus {TID}.",
279                            "TID", tid);
280                 co_return PLDM_ERROR;
281             }
282             isMapped = true;
283         }
284     }
285 
286     if (!isMapped)
287     {
288         // Assigning a tid. If it has been mapped, mapTid()
289         // returns the tid assigned before.
290         auto mappedTid = mapTid(mctpInfo);
291         if (!mappedTid)
292         {
293             lg2::error("Failed to store Terminus Info for terminus {TID}.",
294                        "TID", tid);
295             co_return PLDM_ERROR;
296         }
297 
298         tid = mappedTid.value();
299         rc = co_await setTidOverMctp(eid, tid);
300         if (rc != PLDM_SUCCESS)
301         {
302             lg2::error("Failed to Set terminus TID, error{ERROR}.", "ERROR",
303                        rc);
304             unmapTid(tid);
305             co_return rc;
306         }
307 
308         if (rc != PLDM_SUCCESS && rc != PLDM_ERROR_UNSUPPORTED_PLDM_CMD)
309         {
310             lg2::error("Terminus {TID} does not support SetTID command.", "TID",
311                        tid);
312             unmapTid(tid);
313             co_return rc;
314         }
315 
316         if (termini.contains(tid))
317         {
318             // the terminus has been discovered before
319             co_return PLDM_SUCCESS;
320         }
321     }
322     /* Discovery the mapped terminus */
323     uint64_t supportedTypes = 0;
324     rc = co_await getPLDMTypes(tid, supportedTypes);
325     if (rc)
326     {
327         lg2::error("Failed to Get PLDM Types for terminus {TID}, error {ERROR}",
328                    "TID", tid, "ERROR", rc);
329         unmapTid(tid);
330         co_return PLDM_ERROR;
331     }
332 
333     try
334     {
335         termini[tid] = std::make_shared<Terminus>(tid, supportedTypes);
336     }
337     catch (const sdbusplus::exception_t& e)
338     {
339         lg2::error("Failed to create terminus manager for terminus {TID}",
340                    "TID", tid);
341         unmapTid(tid);
342         co_return PLDM_ERROR;
343     }
344 
345     uint8_t type = PLDM_BASE;
346     auto size = PLDM_MAX_TYPES * (PLDM_MAX_CMDS_PER_TYPE / 8);
347     std::vector<uint8_t> pldmCmds(size);
348     while ((type < PLDM_MAX_TYPES))
349     {
350         if (!termini[tid]->doesSupportType(type))
351         {
352             type++;
353             continue;
354         }
355 
356         ver32_t version{0xFF, 0xFF, 0xFF, 0xFF};
357         auto rc = co_await getPLDMVersion(tid, type, &version);
358         if (rc)
359         {
360             lg2::error(
361                 "Failed to Get PLDM Version for terminus {TID}, PLDM Type {TYPE}, error {ERROR}",
362                 "TID", tid, "TYPE", type, "ERROR", rc);
363         }
364         termini[tid]->setSupportedTypeVersions(type, version);
365         std::vector<bitfield8_t> cmds(PLDM_MAX_CMDS_PER_TYPE / 8);
366         rc = co_await getPLDMCommands(tid, type, version, cmds.data());
367         if (rc)
368         {
369             lg2::error(
370                 "Failed to Get PLDM Commands for terminus {TID}, error {ERROR}",
371                 "TID", tid, "ERROR", rc);
372         }
373 
374         for (size_t i = 0; i < cmds.size(); i++)
375         {
376             auto idx = type * (PLDM_MAX_CMDS_PER_TYPE / 8) + i;
377             if (idx >= pldmCmds.size())
378             {
379                 lg2::error(
380                     "Calculated index {IDX} out of bounds for pldmCmds, type {TYPE}, command index {CMD_IDX}",
381                     "IDX", idx, "TYPE", type, "CMD_IDX", i);
382                 continue;
383             }
384             pldmCmds[idx] = cmds[i].byte;
385         }
386         type++;
387     }
388     termini[tid]->setSupportedCommands(pldmCmds);
389 
390     co_return PLDM_SUCCESS;
391 }
392 
sendRecvPldmMsgOverMctp(mctp_eid_t eid,Request & request,const pldm_msg ** responseMsg,size_t * responseLen)393 exec::task<int> TerminusManager::sendRecvPldmMsgOverMctp(
394     mctp_eid_t eid, Request& request, const pldm_msg** responseMsg,
395     size_t* responseLen)
396 {
397     int rc = 0;
398     try
399     {
400         std::tie(rc, *responseMsg, *responseLen) =
401             co_await handler.sendRecvMsg(eid, std::move(request));
402     }
403     catch (const sdbusplus::exception_t& e)
404     {
405         lg2::error(
406             "Send and Receive PLDM message over MCTP throw error - {ERROR}.",
407             "ERROR", e);
408         co_return PLDM_ERROR;
409     }
410     catch (const int& e)
411     {
412         lg2::error(
413             "Send and Receive PLDM message over MCTP throw int error - {ERROR}.",
414             "ERROR", e);
415         co_return PLDM_ERROR;
416     }
417 
418     co_return rc;
419 }
420 
getTidOverMctp(mctp_eid_t eid,pldm_tid_t * tid)421 exec::task<int> TerminusManager::getTidOverMctp(mctp_eid_t eid, pldm_tid_t* tid)
422 {
423     auto instanceId = instanceIdDb.next(eid);
424     Request request(sizeof(pldm_msg_hdr));
425     auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());
426     auto rc = encode_get_tid_req(instanceId, requestMsg);
427     if (rc)
428     {
429         instanceIdDb.free(eid, instanceId);
430         lg2::error(
431             "Failed to encode request GetTID for endpoint ID {EID}, error {RC} ",
432             "EID", eid, "RC", rc);
433         co_return rc;
434     }
435 
436     const pldm_msg* responseMsg = nullptr;
437     size_t responseLen = 0;
438     rc = co_await sendRecvPldmMsgOverMctp(eid, request, &responseMsg,
439                                           &responseLen);
440     if (rc)
441     {
442         lg2::error("Failed to send GetTID for Endpoint {EID}, error {RC}",
443                    "EID", eid, "RC", rc);
444         co_return rc;
445     }
446 
447     uint8_t completionCode = 0;
448     rc = decode_get_tid_resp(responseMsg, responseLen, &completionCode, tid);
449     if (rc)
450     {
451         lg2::error(
452             "Failed to decode response GetTID for Endpoint ID {EID}, error {RC} ",
453             "EID", eid, "RC", rc);
454         co_return rc;
455     }
456 
457     if (completionCode != PLDM_SUCCESS)
458     {
459         lg2::error("Error : GetTID for Endpoint ID {EID}, complete code {CC}.",
460                    "EID", eid, "CC", completionCode);
461         co_return rc;
462     }
463 
464     co_return completionCode;
465 }
466 
setTidOverMctp(mctp_eid_t eid,pldm_tid_t tid)467 exec::task<int> TerminusManager::setTidOverMctp(mctp_eid_t eid, pldm_tid_t tid)
468 {
469     auto instanceId = instanceIdDb.next(eid);
470     Request request(sizeof(pldm_msg_hdr) + sizeof(pldm_set_tid_req));
471     auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());
472     auto rc = encode_set_tid_req(instanceId, tid, requestMsg);
473     if (rc)
474     {
475         instanceIdDb.free(eid, instanceId);
476         lg2::error(
477             "Failed to encode request SetTID for endpoint ID {EID}, error {RC} ",
478             "EID", eid, "RC", rc);
479         co_return rc;
480     }
481 
482     const pldm_msg* responseMsg = nullptr;
483     size_t responseLen = 0;
484     rc = co_await sendRecvPldmMsgOverMctp(eid, request, &responseMsg,
485                                           &responseLen);
486     if (rc)
487     {
488         lg2::error("Failed to send SetTID for Endpoint {EID}, error {RC}",
489                    "EID", eid, "RC", rc);
490         co_return rc;
491     }
492 
493     if (responseMsg == NULL || responseLen != PLDM_SET_TID_RESP_BYTES)
494     {
495         lg2::error(
496             "Failed to decode response SetTID for Endpoint ID {EID}, error {RC} ",
497             "EID", eid, "RC", rc);
498         co_return PLDM_ERROR_INVALID_LENGTH;
499     }
500 
501     co_return responseMsg->payload[0];
502 }
503 
504 exec::task<int>
getPLDMTypes(pldm_tid_t tid,uint64_t & supportedTypes)505     TerminusManager::getPLDMTypes(pldm_tid_t tid, uint64_t& supportedTypes)
506 {
507     Request request(sizeof(pldm_msg_hdr));
508     auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());
509     auto rc = encode_get_types_req(0, requestMsg);
510     if (rc)
511     {
512         lg2::error(
513             "Failed to encode request getPLDMTypes for terminus ID {TID}, error {RC} ",
514             "TID", tid, "RC", rc);
515         co_return rc;
516     }
517 
518     const pldm_msg* responseMsg = nullptr;
519     size_t responseLen = 0;
520 
521     rc = co_await sendRecvPldmMsg(tid, request, &responseMsg, &responseLen);
522     if (rc)
523     {
524         lg2::error("Failed to send GetPLDMTypes for terminus {TID}, error {RC}",
525                    "TID", tid, "RC", rc);
526         co_return rc;
527     }
528 
529     uint8_t completionCode = 0;
530     bitfield8_t* types = reinterpret_cast<bitfield8_t*>(&supportedTypes);
531     rc =
532         decode_get_types_resp(responseMsg, responseLen, &completionCode, types);
533     if (rc)
534     {
535         lg2::error(
536             "Failed to decode response GetPLDMTypes for terminus ID {TID}, error {RC} ",
537             "TID", tid, "RC", rc);
538         co_return rc;
539     }
540 
541     if (completionCode != PLDM_SUCCESS)
542     {
543         lg2::error(
544             "Error : GetPLDMTypes for terminus ID {TID}, complete code {CC}.",
545             "TID", tid, "CC", completionCode);
546         co_return rc;
547     }
548     co_return completionCode;
549 }
550 
getPLDMCommands(pldm_tid_t tid,uint8_t type,ver32_t version,bitfield8_t * supportedCmds)551 exec::task<int> TerminusManager::getPLDMCommands(
552     pldm_tid_t tid, uint8_t type, ver32_t version, bitfield8_t* supportedCmds)
553 {
554     Request request(sizeof(pldm_msg_hdr) + PLDM_GET_COMMANDS_REQ_BYTES);
555     auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());
556 
557     auto rc = encode_get_commands_req(0, type, version, requestMsg);
558     if (rc)
559     {
560         lg2::error(
561             "Failed to encode request GetPLDMCommands for terminus ID {TID}, error {RC} ",
562             "TID", tid, "RC", rc);
563         co_return rc;
564     }
565 
566     const pldm_msg* responseMsg = nullptr;
567     size_t responseLen = 0;
568 
569     rc = co_await sendRecvPldmMsg(tid, request, &responseMsg, &responseLen);
570     if (rc)
571     {
572         lg2::error(
573             "Failed to send GetPLDMCommands message for terminus {TID}, error {RC}",
574             "TID", tid, "RC", rc);
575         co_return rc;
576     }
577 
578     /* Process response */
579     uint8_t completionCode = 0;
580     rc = decode_get_commands_resp(responseMsg, responseLen, &completionCode,
581                                   supportedCmds);
582     if (rc)
583     {
584         lg2::error(
585             "Failed to decode response GetPLDMCommands for terminus ID {TID}, error {RC} ",
586             "TID", tid, "RC", rc);
587         co_return rc;
588     }
589 
590     if (completionCode != PLDM_SUCCESS)
591     {
592         lg2::error(
593             "Error : GetPLDMCommands for terminus ID {TID}, complete code {CC}.",
594             "TID", tid, "CC", completionCode);
595         co_return rc;
596     }
597 
598     co_return completionCode;
599 }
600 
sendRecvPldmMsg(pldm_tid_t tid,Request & request,const pldm_msg ** responseMsg,size_t * responseLen)601 exec::task<int> TerminusManager::sendRecvPldmMsg(
602     pldm_tid_t tid, Request& request, const pldm_msg** responseMsg,
603     size_t* responseLen)
604 {
605     /**
606      * Size of tidPool is `std::numeric_limits<pldm_tid_t>::max() + 1`
607      * tidPool[i] always exist
608      */
609     if (!tidPool[tid])
610     {
611         co_return PLDM_ERROR_NOT_READY;
612     }
613 
614     if (!transportLayerTable.contains(tid))
615     {
616         co_return PLDM_ERROR_NOT_READY;
617     }
618 
619     if (transportLayerTable[tid] != SupportedTransportLayer::MCTP)
620     {
621         co_return PLDM_ERROR_NOT_READY;
622     }
623 
624     auto mctpInfo = toMctpInfo(tid);
625     if (!mctpInfo.has_value())
626     {
627         co_return PLDM_ERROR_NOT_READY;
628     }
629 
630     auto eid = std::get<0>(mctpInfo.value());
631     auto requestMsg = reinterpret_cast<pldm_msg*>(request.data());
632     requestMsg->hdr.instance_id = instanceIdDb.next(eid);
633     auto rc = co_await sendRecvPldmMsgOverMctp(eid, request, responseMsg,
634                                                responseLen);
635 
636     co_return rc;
637 }
638 
getPLDMVersion(pldm_tid_t tid,uint8_t type,ver32_t * version)639 exec::task<int> TerminusManager::getPLDMVersion(pldm_tid_t tid, uint8_t type,
640                                                 ver32_t* version)
641 {
642     Request request(sizeof(pldm_msg_hdr) + PLDM_GET_VERSION_REQ_BYTES);
643     auto requestMsg = new (request.data()) pldm_msg;
644 
645     auto rc =
646         encode_get_version_req(0, 0, PLDM_GET_FIRSTPART, type, requestMsg);
647     if (rc)
648     {
649         lg2::error(
650             "Failed to encode request getPLDMVersion for terminus ID {TID}, error {RC} ",
651             "TID", tid, "RC", rc);
652         co_return rc;
653     }
654 
655     const pldm_msg* responseMsg = nullptr;
656     size_t responseLen = 0;
657 
658     rc = co_await sendRecvPldmMsg(tid, request, &responseMsg, &responseLen);
659     if (rc)
660     {
661         lg2::error(
662             "Failed to send getPLDMVersion message for terminus {TID}, error {RC}",
663             "TID", tid, "RC", rc);
664         co_return rc;
665     }
666 
667     /* Process response */
668     uint8_t completionCode = 0;
669     uint8_t transferFlag = 0;
670     uint32_t transferHandle = 0;
671     rc = decode_get_version_resp(responseMsg, responseLen, &completionCode,
672                                  &transferHandle, &transferFlag, version);
673     if (rc)
674     {
675         lg2::error(
676             "Failed to decode response getPLDMVersion for terminus ID {TID}, error {RC} ",
677             "TID", tid, "RC", rc);
678         co_return rc;
679     }
680 
681     if (completionCode != PLDM_SUCCESS)
682     {
683         lg2::error(
684             "Error : getPLDMVersion for terminus ID {TID}, complete code {CC}.",
685             "TID", tid, "CC", completionCode);
686         co_return completionCode;
687     }
688 
689     co_return completionCode;
690 }
691 
692 } // namespace platform_mc
693 } // namespace pldm
694