xref: /openbmc/pldm/requester/handler.hpp (revision f7d249ed)
1 #pragma once
2 
3 #include "common/instance_id.hpp"
4 #include "common/transport.hpp"
5 #include "common/types.hpp"
6 #include "request.hpp"
7 
8 #include <libpldm/base.h>
9 #include <sys/socket.h>
10 
11 #include <phosphor-logging/lg2.hpp>
12 #include <sdbusplus/async.hpp>
13 #include <sdbusplus/timer.hpp>
14 #include <sdeventplus/event.hpp>
15 #include <sdeventplus/source/event.hpp>
16 
17 #include <cassert>
18 #include <chrono>
19 #include <deque>
20 #include <functional>
21 #include <memory>
22 #include <mutex>
23 #include <queue>
24 #include <tuple>
25 #include <unordered_map>
26 
27 PHOSPHOR_LOG2_USING;
28 
29 namespace pldm
30 {
31 namespace requester
32 {
33 /** @struct RequestKey
34  *
35  *  RequestKey uniquely identifies the PLDM request message to match it with the
36  *  response and a combination of MCTP endpoint ID, PLDM instance ID, PLDM type
37  *  and PLDM command is the key.
38  */
39 struct RequestKey
40 {
41     mctp_eid_t eid;     //!< MCTP endpoint ID
42     uint8_t instanceId; //!< PLDM instance ID
43     uint8_t type;       //!< PLDM type
44     uint8_t command;    //!< PLDM command
45 
operator ==pldm::requester::RequestKey46     bool operator==(const RequestKey& e) const
47     {
48         return ((eid == e.eid) && (instanceId == e.instanceId) &&
49                 (type == e.type) && (command == e.command));
50     }
51 };
52 
53 /** @struct RequestKeyHasher
54  *
55  *  This is a simple hash function, since the instance ID generator API
56  *  generates unique instance IDs for MCTP endpoint ID.
57  */
58 struct RequestKeyHasher
59 {
operator ()pldm::requester::RequestKeyHasher60     std::size_t operator()(const RequestKey& key) const
61     {
62         return (key.eid << 24 | key.instanceId << 16 | key.type << 8 |
63                 key.command);
64     }
65 };
66 
67 using ResponseHandler = std::function<void(
68     mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen)>;
69 
70 /** @brief The response from SendRecvMsg with coroutine API
71  *
72  *  The response when registers PLDM request message using the SendRecvMsg
73  *  with coroutine API.
74  *  Responded tuple includes <CompleteCode, ResponseMgs, ResponseMsgLength>
75  *  Value: [PLDM_ERROR, _, _] if registerRequest fails.
76  *         [PLDM_ERROR_NOT_READY, nullptr, 0] if timed out.
77  *         [PLDM_SUCCESS, ResponseMsg, ResponseMsgLength] if succeeded
78  */
79 using SendRecvCoResp = std::tuple<int, const pldm_msg*, size_t>;
80 
81 /** @struct RegisteredRequest
82  *
83  *  This struct is used to store the registered request to one endpoint.
84  */
85 struct RegisteredRequest
86 {
87     RequestKey key;                  //!< Responder MCTP endpoint ID
88     std::vector<uint8_t> reqMsg;     //!< Request messages queue
89     ResponseHandler responseHandler; //!< Waiting for response flag
90 };
91 
92 /** @struct EndpointMessageQueue
93  *
94  *  This struct is used to save the list of request messages of one endpoint and
95  *  the existing of the request message to the endpoint with its' EID.
96  */
97 struct EndpointMessageQueue
98 {
99     mctp_eid_t eid; //!< Responder MCTP endpoint ID
100     std::deque<std::shared_ptr<RegisteredRequest>> requestQueue; //!< Queue
101     bool activeRequest; //!< Waiting for response flag
102 
operator ==pldm::requester::EndpointMessageQueue103     bool operator==(const mctp_eid_t& mctpEid) const
104     {
105         return (eid == mctpEid);
106     }
107 };
108 
109 /** @class Handler
110  *
111  *  This class handles the lifecycle of the PLDM request message based on the
112  *  instance ID expiration interval, number of request retries and the timeout
113  *  waiting for a response. The registered response handlers are invoked with
114  *  response once the PLDM responder sends the response. If no response is
115  *  received within the instance ID expiration interval or any other failure the
116  *  response handler is invoked with the empty response.
117  *
118  * @tparam RequestInterface - Request class type
119  */
120 template <class RequestInterface>
121 class Handler
122 {
123   public:
124     Handler() = delete;
125     Handler(const Handler&) = delete;
126     Handler(Handler&&) = delete;
127     Handler& operator=(const Handler&) = delete;
128     Handler& operator=(Handler&&) = delete;
129     ~Handler() = default;
130 
131     /** @brief Constructor
132      *
133      *  @param[in] pldm_transport - PLDM requester
134      *  @param[in] event - reference to PLDM daemon's main event loop
135      *  @param[in] instanceIdDb - reference to an InstanceIdDb
136      *  @param[in] verbose - verbose tracing flag
137      *  @param[in] instanceIdExpiryInterval - instance ID expiration interval
138      *  @param[in] numRetries - number of request retries
139      *  @param[in] responseTimeOut - time to wait between each retry
140      */
Handler(PldmTransport * pldmTransport,sdeventplus::Event & event,pldm::InstanceIdDb & instanceIdDb,bool verbose,std::chrono::seconds instanceIdExpiryInterval=std::chrono::seconds (INSTANCE_ID_EXPIRATION_INTERVAL),uint8_t numRetries=static_cast<uint8_t> (NUMBER_OF_REQUEST_RETRIES),std::chrono::milliseconds responseTimeOut=std::chrono::milliseconds (RESPONSE_TIME_OUT))141     explicit Handler(
142         PldmTransport* pldmTransport, sdeventplus::Event& event,
143         pldm::InstanceIdDb& instanceIdDb, bool verbose,
144         std::chrono::seconds instanceIdExpiryInterval =
145             std::chrono::seconds(INSTANCE_ID_EXPIRATION_INTERVAL),
146         uint8_t numRetries = static_cast<uint8_t>(NUMBER_OF_REQUEST_RETRIES),
147         std::chrono::milliseconds responseTimeOut =
148             std::chrono::milliseconds(RESPONSE_TIME_OUT)) :
149         pldmTransport(pldmTransport), event(event), instanceIdDb(instanceIdDb),
150         verbose(verbose), instanceIdExpiryInterval(instanceIdExpiryInterval),
151         numRetries(numRetries), responseTimeOut(responseTimeOut)
152     {}
153 
instanceIdExpiryCallBack(RequestKey key)154     void instanceIdExpiryCallBack(RequestKey key)
155     {
156         auto eid = key.eid;
157         if (this->handlers.contains(key))
158         {
159             info(
160                 "Instance ID expiry for EID '{EID}' using InstanceID '{INSTANCEID}'",
161                 "EID", key.eid, "INSTANCEID", key.instanceId);
162             auto& [request, responseHandler,
163                    timerInstance] = this->handlers[key];
164             request->stop();
165             auto rc = timerInstance->stop();
166             if (rc)
167             {
168                 error(
169                     "Failed to stop the instance ID expiry timer, response code '{RC}'",
170                     "RC", rc);
171             }
172             // Call response handler with an empty response to indicate no
173             // response
174             responseHandler(eid, nullptr, 0);
175             this->removeRequestContainer.emplace(
176                 key,
177                 std::make_unique<sdeventplus::source::Defer>(
178                     event, std::bind(&Handler::removeRequestEntry, this, key)));
179             endpointMessageQueues[eid]->activeRequest = false;
180 
181             /* try to send new request if the endpoint is free */
182             pollEndpointQueue(eid);
183         }
184         else
185         {
186             // This condition is not possible, if a response is received
187             // before the instance ID expiry, then the response handler
188             // is executed and the entry will be removed.
189             assert(false);
190         }
191     }
192 
193     /** @brief Send the remaining PLDM request messages in endpoint queue
194      *
195      *  @param[in] eid - endpoint ID of the remote MCTP endpoint
196      */
pollEndpointQueue(mctp_eid_t eid)197     int pollEndpointQueue(mctp_eid_t eid)
198     {
199         if (endpointMessageQueues[eid]->activeRequest ||
200             endpointMessageQueues[eid]->requestQueue.empty())
201         {
202             return PLDM_SUCCESS;
203         }
204 
205         endpointMessageQueues[eid]->activeRequest = true;
206         auto requestMsg = endpointMessageQueues[eid]->requestQueue.front();
207         endpointMessageQueues[eid]->requestQueue.pop_front();
208 
209         auto request = std::make_unique<RequestInterface>(
210             pldmTransport, requestMsg->key.eid, event,
211             std::move(requestMsg->reqMsg), numRetries, responseTimeOut,
212             verbose);
213         auto timer = std::make_unique<sdbusplus::Timer>(
214             event.get(), std::bind(&Handler::instanceIdExpiryCallBack, this,
215                                    requestMsg->key));
216 
217         auto rc = request->start();
218         if (rc)
219         {
220             instanceIdDb.free(requestMsg->key.eid, requestMsg->key.instanceId);
221             error(
222                 "Failure to send the PLDM request message for polling endpoint queue, response code '{RC}'",
223                 "RC", rc);
224             endpointMessageQueues[eid]->activeRequest = false;
225             return rc;
226         }
227 
228         try
229         {
230             timer->start(duration_cast<std::chrono::microseconds>(
231                 instanceIdExpiryInterval));
232         }
233         catch (const std::runtime_error& e)
234         {
235             instanceIdDb.free(requestMsg->key.eid, requestMsg->key.instanceId);
236             error(
237                 "Failed to start the instance ID expiry timer, error - {ERROR}",
238                 "ERROR", e);
239             endpointMessageQueues[eid]->activeRequest = false;
240             return PLDM_ERROR;
241         }
242 
243         handlers.emplace(requestMsg->key,
244                          std::make_tuple(std::move(request),
245                                          std::move(requestMsg->responseHandler),
246                                          std::move(timer)));
247         return PLDM_SUCCESS;
248     }
249 
250     /** @brief Register a PLDM request message
251      *
252      *  @param[in] eid - endpoint ID of the remote MCTP endpoint
253      *  @param[in] instanceId - instance ID to match request and response
254      *  @param[in] type - PLDM type
255      *  @param[in] command - PLDM command
256      *  @param[in] requestMsg - PLDM request message
257      *  @param[in] responseHandler - Response handler for this request
258      *
259      *  @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise
260      */
registerRequest(mctp_eid_t eid,uint8_t instanceId,uint8_t type,uint8_t command,pldm::Request && requestMsg,ResponseHandler && responseHandler)261     int registerRequest(mctp_eid_t eid, uint8_t instanceId, uint8_t type,
262                         uint8_t command, pldm::Request&& requestMsg,
263                         ResponseHandler&& responseHandler)
264     {
265         RequestKey key{eid, instanceId, type, command};
266 
267         if (handlers.contains(key))
268         {
269             error(
270                 "Register request for EID '{EID}' is using InstanceID '{INSTANCEID}'",
271                 "EID", eid, "INSTANCEID", instanceId);
272             return PLDM_ERROR;
273         }
274 
275         auto inputRequest = std::make_shared<RegisteredRequest>(
276             key, std::move(requestMsg), std::move(responseHandler));
277         if (endpointMessageQueues.contains(eid))
278         {
279             endpointMessageQueues[eid]->requestQueue.push_back(inputRequest);
280         }
281         else
282         {
283             std::deque<std::shared_ptr<RegisteredRequest>> reqQueue;
284             reqQueue.push_back(inputRequest);
285             endpointMessageQueues[eid] =
286                 std::make_shared<EndpointMessageQueue>(eid, reqQueue, false);
287         }
288 
289         /* try to send new request if the endpoint is free */
290         pollEndpointQueue(eid);
291 
292         return PLDM_SUCCESS;
293     }
294 
295     /** @brief Unregister a PLDM request message
296      *
297      *  @param[in] eid - endpoint ID of the remote MCTP endpoint
298      *  @param[in] instanceId - instance ID to match request and response
299      *  @param[in] type - PLDM type
300      *  @param[in] command - PLDM command
301      *
302      *  @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise
303      */
unregisterRequest(mctp_eid_t eid,uint8_t instanceId,uint8_t type,uint8_t command)304     int unregisterRequest(mctp_eid_t eid, uint8_t instanceId, uint8_t type,
305                           uint8_t command)
306     {
307         RequestKey key{eid, instanceId, type, command};
308 
309         /* handlers only contain key when the message is already sent */
310         if (handlers.contains(key))
311         {
312             auto& [request, responseHandler, timerInstance] = handlers[key];
313             request->stop();
314             auto rc = timerInstance->stop();
315             if (rc)
316             {
317                 error(
318                     "Failed to stop the instance ID expiry timer, response code '{RC}'",
319                     "RC", static_cast<int>(rc));
320             }
321 
322             instanceIdDb.free(key.eid, key.instanceId);
323             handlers.erase(key);
324             endpointMessageQueues[eid]->activeRequest = false;
325             /* try to send new request if the endpoint is free */
326             pollEndpointQueue(eid);
327 
328             return PLDM_SUCCESS;
329         }
330         else
331         {
332             if (!endpointMessageQueues.contains(eid))
333             {
334                 error(
335                     "Can't find request for EID '{EID}' is using InstanceID '{INSTANCEID}' in Endpoint message Queue",
336                     "EID", (unsigned)eid, "INSTANCEID", (unsigned)instanceId);
337                 return PLDM_ERROR;
338             }
339             auto requestMsg = endpointMessageQueues[eid]->requestQueue;
340             /* Find the registered request in the requestQueue */
341             for (auto it = requestMsg.begin(); it != requestMsg.end();)
342             {
343                 auto msg = *it;
344                 if (msg->key == key)
345                 {
346                     // erase and get the next valid iterator
347                     it = endpointMessageQueues[eid]->requestQueue.erase(it);
348                     instanceIdDb.free(key.eid, key.instanceId);
349                     return PLDM_SUCCESS;
350                 }
351                 else
352                 {
353                     ++it; // increment iterator only if not erasing
354                 }
355             }
356         }
357 
358         return PLDM_ERROR;
359     }
360 
361     /** @brief Handle PLDM response message
362      *
363      *  @param[in] eid - endpoint ID of the remote MCTP endpoint
364      *  @param[in] instanceId - instance ID to match request and response
365      *  @param[in] type - PLDM type
366      *  @param[in] command - PLDM command
367      *  @param[in] response - PLDM response message
368      *  @param[in] respMsgLen - length of the response message
369      */
handleResponse(mctp_eid_t eid,uint8_t instanceId,uint8_t type,uint8_t command,const pldm_msg * response,size_t respMsgLen)370     void handleResponse(mctp_eid_t eid, uint8_t instanceId, uint8_t type,
371                         uint8_t command, const pldm_msg* response,
372                         size_t respMsgLen)
373     {
374         RequestKey key{eid, instanceId, type, command};
375         if (handlers.contains(key) && !removeRequestContainer.contains(key))
376         {
377             auto& [request, responseHandler, timerInstance] = handlers[key];
378             request->stop();
379             auto rc = timerInstance->stop();
380             if (rc)
381             {
382                 error(
383                     "Failed to stop the instance ID expiry timer, response code '{RC}'",
384                     "RC", rc);
385             }
386             responseHandler(eid, response, respMsgLen);
387             instanceIdDb.free(key.eid, key.instanceId);
388             handlers.erase(key);
389 
390             endpointMessageQueues[eid]->activeRequest = false;
391             /* try to send new request if the endpoint is free */
392             pollEndpointQueue(eid);
393         }
394         else
395         {
396             // Got a response for a PLDM request message not registered with the
397             // request handler, so freeing up the instance ID, this can be other
398             // OpenBMC applications relying on PLDM D-Bus apis like
399             // openpower-occ-control and softoff
400             instanceIdDb.free(key.eid, key.instanceId);
401         }
402     }
403 
404     /** @brief Wrap registerRequest with coroutine API.
405      *
406      *  @return Return [PLDM_ERROR, _, _] if registerRequest fails.
407      *          Return [PLDM_ERROR_NOT_READY, nullptr, 0] if timed out.
408      *          Return [PLDM_SUCCESS, resp, len] if succeeded
409      */
410     stdexec::sender_of<stdexec::set_value_t(SendRecvCoResp)> auto
411         sendRecvMsg(mctp_eid_t eid, pldm::Request&& request);
412 
413   private:
414     PldmTransport* pldmTransport; //!< PLDM transport object
415     sdeventplus::Event& event; //!< reference to PLDM daemon's main event loop
416     pldm::InstanceIdDb& instanceIdDb; //!< reference to an InstanceIdDb
417     bool verbose;                     //!< verbose tracing flag
418     std::chrono::seconds
419         instanceIdExpiryInterval;     //!< Instance ID expiration interval
420     uint8_t numRetries;               //!< number of request retries
421     std::chrono::milliseconds
422         responseTimeOut;              //!< time to wait between each retry
423 
424     /** @brief Container for storing the details of the PLDM request
425      *         message, handler for the corresponding PLDM response and the
426      *         timer object for the Instance ID expiration
427      */
428     using RequestValue =
429         std::tuple<std::unique_ptr<RequestInterface>, ResponseHandler,
430                    std::unique_ptr<sdbusplus::Timer>>;
431 
432     // Manage the requests of responders base on MCTP EID
433     std::map<mctp_eid_t, std::shared_ptr<EndpointMessageQueue>>
434         endpointMessageQueues;
435 
436     /** @brief Container for storing the PLDM request entries */
437     std::unordered_map<RequestKey, RequestValue, RequestKeyHasher> handlers;
438 
439     /** @brief Container to store information about the request entries to be
440      *         removed after the instance ID timer expires
441      */
442     std::unordered_map<RequestKey, std::unique_ptr<sdeventplus::source::Defer>,
443                        RequestKeyHasher>
444         removeRequestContainer;
445 
446     /** @brief Remove request entry for which the instance ID expired
447      *
448      *  @param[in] key - key for the Request
449      */
removeRequestEntry(RequestKey key)450     void removeRequestEntry(RequestKey key)
451     {
452         if (removeRequestContainer.contains(key))
453         {
454             removeRequestContainer[key].reset();
455             instanceIdDb.free(key.eid, key.instanceId);
456             handlers.erase(key);
457             removeRequestContainer.erase(key);
458         }
459     }
460 };
461 
462 /** @class SendRecvMsgOperation
463  *
464  *  Represents the state and logic for a single send/receive message operation
465  *
466  * @tparam RequestInterface - Request class type
467  * @tparam stdexec::receiver - Execute receiver
468  */
469 template <class RequestInterface, stdexec::receiver R>
470 struct SendRecvMsgOperation
471 {
472     SendRecvMsgOperation() = delete;
473 
SendRecvMsgOperationpldm::requester::SendRecvMsgOperation474     explicit SendRecvMsgOperation(Handler<RequestInterface>& handler,
475                                   mctp_eid_t eid, pldm::Request&& request,
476                                   R&& r) :
477         handler(handler), request(std::move(request)), receiver(std::move(r))
478     {
479         auto requestMsg =
480             reinterpret_cast<const pldm_msg*>(this->request.data());
481         requestKey = RequestKey{
482             eid,
483             requestMsg->hdr.instance_id,
484             requestMsg->hdr.type,
485             requestMsg->hdr.command,
486         };
487         response = nullptr;
488         respMsgLen = 0;
489     }
490 
491     /** @brief Checks if the operation has been requested to stop.
492      *         If so, it sets the state to stopped.Registers the request with
493      *         the handler. If registration fails, sets an error on the
494      *         receiver. If stopping is possible, sets up a stop callback.
495      *
496      *  @param[in] op - operation request
497      *
498      *  @return Execute errors
499      */
tag_invoke(stdexec::start_t,SendRecvMsgOperation & op)500     friend void tag_invoke(stdexec::start_t, SendRecvMsgOperation& op) noexcept
501     {
502         auto stopToken = stdexec::get_stop_token(stdexec::get_env(op.receiver));
503 
504         // operation already cancelled
505         if (stopToken.stop_requested())
506         {
507             return stdexec::set_stopped(std::move(op.receiver));
508         }
509 
510         using namespace std::placeholders;
511         auto rc = op.handler.registerRequest(
512             op.requestKey.eid, op.requestKey.instanceId, op.requestKey.type,
513             op.requestKey.command, std::move(op.request),
514             std::bind(&SendRecvMsgOperation::onComplete, &op, _1, _2, _3));
515         if (rc)
516         {
517             return stdexec::set_value(std::move(op.receiver), rc,
518                                       static_cast<const pldm_msg*>(nullptr),
519                                       static_cast<size_t>(0));
520         }
521 
522         if (stopToken.stop_possible())
523         {
524             op.stopCallback.emplace(
525                 std::move(stopToken),
526                 std::bind(&SendRecvMsgOperation::onStop, &op));
527         }
528     }
529 
530     /** @brief Unregisters the request and sets the state to stopped on the
531      *         receiver.
532      */
onStoppldm::requester::SendRecvMsgOperation533     void onStop()
534     {
535         handler.unregisterRequest(requestKey.eid, requestKey.instanceId,
536                                   requestKey.type, requestKey.command);
537         return stdexec::set_stopped(std::move(receiver));
538     }
539 
540     /** @brief This function resets the stop callback. Validates the response
541      *         and sets either an error or a value on the receiver.
542      *
543      *  @param[in] eid - endpoint ID of the remote MCTP endpoint
544      *  @param[in] response - PLDM response message
545      *  @param[in] respMsgLen - length of the response message
546      *
547      *  @return PLDM completion code
548      */
onCompletepldm::requester::SendRecvMsgOperation549     void onComplete(mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen)
550     {
551         stopCallback.reset();
552         assert(eid == this->requestKey.eid);
553         auto rc = PLDM_SUCCESS;
554         if (!response && !respMsgLen)
555         {
556             rc = PLDM_ERROR_NOT_READY;
557         }
558         return stdexec::set_value(std::move(receiver), static_cast<int>(rc),
559                                   response, respMsgLen);
560     }
561 
562   private:
563     /** @brief Reference to a Handler object that manages the request/response
564      *         logic.
565      */
566     requester::Handler<RequestInterface>& handler;
567 
568     /** @brief Stores information about the request such as eid, instanceId,
569      *         type, and command.
570      */
571     RequestKey requestKey;
572 
573     /** @brief The request message to be sent.
574      */
575     pldm::Request request;
576 
577     /** @brief The response message for the sent request message.
578      */
579     const pldm_msg* response;
580 
581     /** @brief The length of response message for the sent request message.
582      */
583     size_t respMsgLen;
584 
585     /** @brief The receiver to be notified with the result of the operation.
586      */
587     R receiver;
588 
589     /** @brief An optional callback that handles stopping the operation if
590      *         requested.
591      */
592     std::optional<typename stdexec::stop_token_of_t<
593         stdexec::env_of_t<R>>::template callback_type<std::function<void()>>>
594         stopCallback = std::nullopt;
595 };
596 
597 /** @class SendRecvMsgSender
598  *
599  *  Represents the single message sender
600  *
601  * @tparam RequestInterface - Request class type
602  */
603 template <class RequestInterface>
604 struct SendRecvMsgSender
605 {
606     using is_sender = void;
607 
608     SendRecvMsgSender() = delete;
609 
SendRecvMsgSenderpldm::requester::SendRecvMsgSender610     explicit SendRecvMsgSender(requester::Handler<RequestInterface>& handler,
611                                mctp_eid_t eid, pldm::Request&& request) :
612         handler(handler), eid(eid), request(std::move(request))
613     {}
614 
615     friend auto tag_invoke(stdexec::get_completion_signatures_t,
616                            const SendRecvMsgSender&, auto)
617         -> stdexec::completion_signatures<
618             stdexec::set_value_t(int, const pldm_msg*, size_t),
619             stdexec::set_stopped_t()>;
620 
621     /** @brief Execute the sending the request message */
622     template <stdexec::receiver R>
tag_invoke(stdexec::connect_t,SendRecvMsgSender && self,R r)623     friend auto tag_invoke(stdexec::connect_t, SendRecvMsgSender&& self, R r)
624     {
625         return SendRecvMsgOperation<RequestInterface, R>(
626             self.handler, self.eid, std::move(self.request), std::move(r));
627     }
628 
629   private:
630     /** @brief Reference to a Handler object that manages the request/response
631      *         logic.
632      */
633     requester::Handler<RequestInterface>& handler;
634 
635     /** @brief MCTP Endpoint ID of request message */
636     mctp_eid_t eid;
637 
638     /** @brief Request message */
639     pldm::Request request;
640 };
641 
642 /** @brief Wrap registerRequest with coroutine API.
643  *
644  *  @param[in] eid - endpoint ID of the remote MCTP endpoint
645  *  @param[in] request - PLDM request message
646  *
647  *  @return Return [PLDM_ERROR, _, _] if registerRequest fails.
648  *          Return [PLDM_ERROR_NOT_READY, nullptr, 0] if timed out.
649  *          Return [PLDM_SUCCESS, resp, len] if succeeded
650  */
651 template <class RequestInterface>
652 stdexec::sender_of<stdexec::set_value_t(SendRecvCoResp)> auto
sendRecvMsg(mctp_eid_t eid,pldm::Request && request)653     Handler<RequestInterface>::sendRecvMsg(mctp_eid_t eid,
654                                            pldm::Request&& request)
655 {
656     return SendRecvMsgSender(*this, eid, std::move(request)) |
657            stdexec::then([](int rc, const pldm_msg* resp, size_t respLen) {
658                return std::make_tuple(rc, resp, respLen);
659            });
660 }
661 
662 } // namespace requester
663 
664 } // namespace pldm
665