xref: /openbmc/pldm/requester/handler.hpp (revision 059fc8922b5fc26f35fbe219dd64369552c14bfb)
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         auto rc = pollEndpointQueue(eid);
291         if (rc != PLDM_SUCCESS)
292         {
293             error(
294                 "Failed to process request queue for EID {EID}, response code {RC}.",
295                 "EID", eid, "RC", rc);
296             return rc;
297         }
298 
299         return PLDM_SUCCESS;
300     }
301 
302     /** @brief Unregister a PLDM request message
303      *
304      *  @param[in] eid - endpoint ID of the remote MCTP endpoint
305      *  @param[in] instanceId - instance ID to match request and response
306      *  @param[in] type - PLDM type
307      *  @param[in] command - PLDM command
308      *
309      *  @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise
310      */
unregisterRequest(mctp_eid_t eid,uint8_t instanceId,uint8_t type,uint8_t command)311     int unregisterRequest(mctp_eid_t eid, uint8_t instanceId, uint8_t type,
312                           uint8_t command)
313     {
314         RequestKey key{eid, instanceId, type, command};
315 
316         /* handlers only contain key when the message is already sent */
317         if (handlers.contains(key))
318         {
319             auto& [request, responseHandler, timerInstance] = handlers[key];
320             request->stop();
321             auto rc = timerInstance->stop();
322             if (rc)
323             {
324                 error(
325                     "Failed to stop the instance ID expiry timer, response code '{RC}'",
326                     "RC", static_cast<int>(rc));
327             }
328 
329             instanceIdDb.free(key.eid, key.instanceId);
330             handlers.erase(key);
331             endpointMessageQueues[eid]->activeRequest = false;
332             /* try to send new request if the endpoint is free */
333             pollEndpointQueue(eid);
334 
335             return PLDM_SUCCESS;
336         }
337         else
338         {
339             if (!endpointMessageQueues.contains(eid))
340             {
341                 error(
342                     "Can't find request for EID '{EID}' is using InstanceID '{INSTANCEID}' in Endpoint message Queue",
343                     "EID", (unsigned)eid, "INSTANCEID", (unsigned)instanceId);
344                 return PLDM_ERROR;
345             }
346             auto requestMsg = endpointMessageQueues[eid]->requestQueue;
347             /* Find the registered request in the requestQueue */
348             for (auto it = requestMsg.begin(); it != requestMsg.end();)
349             {
350                 auto msg = *it;
351                 if (msg->key == key)
352                 {
353                     // erase and get the next valid iterator
354                     it = endpointMessageQueues[eid]->requestQueue.erase(it);
355                     instanceIdDb.free(key.eid, key.instanceId);
356                     return PLDM_SUCCESS;
357                 }
358                 else
359                 {
360                     ++it; // increment iterator only if not erasing
361                 }
362             }
363         }
364 
365         return PLDM_ERROR;
366     }
367 
368     /** @brief Handle PLDM response message
369      *
370      *  @param[in] eid - endpoint ID of the remote MCTP endpoint
371      *  @param[in] instanceId - instance ID to match request and response
372      *  @param[in] type - PLDM type
373      *  @param[in] command - PLDM command
374      *  @param[in] response - PLDM response message
375      *  @param[in] respMsgLen - length of the response message
376      */
handleResponse(mctp_eid_t eid,uint8_t instanceId,uint8_t type,uint8_t command,const pldm_msg * response,size_t respMsgLen)377     void handleResponse(mctp_eid_t eid, uint8_t instanceId, uint8_t type,
378                         uint8_t command, const pldm_msg* response,
379                         size_t respMsgLen)
380     {
381         RequestKey key{eid, instanceId, type, command};
382         if (handlers.contains(key) && !removeRequestContainer.contains(key))
383         {
384             auto& [request, responseHandler, timerInstance] = handlers[key];
385             request->stop();
386             auto rc = timerInstance->stop();
387             if (rc)
388             {
389                 error(
390                     "Failed to stop the instance ID expiry timer, response code '{RC}'",
391                     "RC", rc);
392             }
393             responseHandler(eid, response, respMsgLen);
394             instanceIdDb.free(key.eid, key.instanceId);
395             handlers.erase(key);
396 
397             endpointMessageQueues[eid]->activeRequest = false;
398             /* try to send new request if the endpoint is free */
399             pollEndpointQueue(eid);
400         }
401     }
402 
403     /** @brief Wrap registerRequest with coroutine API.
404      *
405      *  @return Return [PLDM_ERROR, _, _] if registerRequest fails.
406      *          Return [PLDM_ERROR_NOT_READY, nullptr, 0] if timed out.
407      *          Return [PLDM_SUCCESS, resp, len] if succeeded
408      */
409     stdexec::sender_of<stdexec::set_value_t(SendRecvCoResp)> auto sendRecvMsg(
410         mctp_eid_t eid, pldm::Request&& request);
411 
412   private:
413     PldmTransport* pldmTransport; //!< PLDM transport object
414     sdeventplus::Event& event; //!< reference to PLDM daemon's main event loop
415     pldm::InstanceIdDb& instanceIdDb; //!< reference to an InstanceIdDb
416     bool verbose;                     //!< verbose tracing flag
417     std::chrono::seconds
418         instanceIdExpiryInterval;     //!< Instance ID expiration interval
419     uint8_t numRetries;               //!< number of request retries
420     std::chrono::milliseconds
421         responseTimeOut;              //!< time to wait between each retry
422 
423     /** @brief Container for storing the details of the PLDM request
424      *         message, handler for the corresponding PLDM response and the
425      *         timer object for the Instance ID expiration
426      */
427     using RequestValue =
428         std::tuple<std::unique_ptr<RequestInterface>, ResponseHandler,
429                    std::unique_ptr<sdbusplus::Timer>>;
430 
431     // Manage the requests of responders base on MCTP EID
432     std::map<mctp_eid_t, std::shared_ptr<EndpointMessageQueue>>
433         endpointMessageQueues;
434 
435     /** @brief Container for storing the PLDM request entries */
436     std::unordered_map<RequestKey, RequestValue, RequestKeyHasher> handlers;
437 
438     /** @brief Container to store information about the request entries to be
439      *         removed after the instance ID timer expires
440      */
441     std::unordered_map<RequestKey, std::unique_ptr<sdeventplus::source::Defer>,
442                        RequestKeyHasher>
443         removeRequestContainer;
444 
445     /** @brief Remove request entry for which the instance ID expired
446      *
447      *  @param[in] key - key for the Request
448      */
removeRequestEntry(RequestKey key)449     void removeRequestEntry(RequestKey key)
450     {
451         if (removeRequestContainer.contains(key))
452         {
453             removeRequestContainer[key].reset();
454             instanceIdDb.free(key.eid, key.instanceId);
455             handlers.erase(key);
456             removeRequestContainer.erase(key);
457         }
458     }
459 };
460 
461 /** @class SendRecvMsgOperation
462  *
463  *  Represents the state and logic for a single send/receive message operation
464  *
465  * @tparam RequestInterface - Request class type
466  * @tparam stdexec::receiver - Execute receiver
467  */
468 template <class RequestInterface, stdexec::receiver R>
469 struct SendRecvMsgOperation
470 {
471     SendRecvMsgOperation() = delete;
472 
SendRecvMsgOperationpldm::requester::SendRecvMsgOperation473     explicit SendRecvMsgOperation(Handler<RequestInterface>& handler,
474                                   mctp_eid_t eid, pldm::Request&& request,
475                                   R&& r) :
476         handler(handler), request(std::move(request)), receiver(std::move(r))
477     {
478         auto requestMsg =
479             reinterpret_cast<const pldm_msg*>(this->request.data());
480         requestKey = RequestKey{
481             eid,
482             requestMsg->hdr.instance_id,
483             requestMsg->hdr.type,
484             requestMsg->hdr.command,
485         };
486         response = nullptr;
487         respMsgLen = 0;
488     }
489 
490     /** @brief Checks if the operation has been requested to stop.
491      *         If so, it sets the state to stopped.Registers the request with
492      *         the handler. If registration fails, sets an error on the
493      *         receiver. If stopping is possible, sets up a stop callback.
494      *
495      *  @param[in] op - operation request
496      *
497      *  @return Execute errors
498      */
tag_invoke(stdexec::start_t,SendRecvMsgOperation & op)499     friend void tag_invoke(stdexec::start_t, SendRecvMsgOperation& op) noexcept
500     {
501         auto stopToken = stdexec::get_stop_token(stdexec::get_env(op.receiver));
502 
503         // operation already cancelled
504         if (stopToken.stop_requested())
505         {
506             return stdexec::set_stopped(std::move(op.receiver));
507         }
508 
509         using namespace std::placeholders;
510         auto rc = op.handler.registerRequest(
511             op.requestKey.eid, op.requestKey.instanceId, op.requestKey.type,
512             op.requestKey.command, std::move(op.request),
513             std::bind(&SendRecvMsgOperation::onComplete, &op, _1, _2, _3));
514         if (rc)
515         {
516             return stdexec::set_value(std::move(op.receiver), rc,
517                                       static_cast<const pldm_msg*>(nullptr),
518                                       static_cast<size_t>(0));
519         }
520 
521         if (stopToken.stop_possible())
522         {
523             op.stopCallback.emplace(
524                 std::move(stopToken),
525                 std::bind(&SendRecvMsgOperation::onStop, &op));
526         }
527     }
528 
529     /** @brief Unregisters the request and sets the state to stopped on the
530      *         receiver.
531      */
onStoppldm::requester::SendRecvMsgOperation532     void onStop()
533     {
534         handler.unregisterRequest(requestKey.eid, requestKey.instanceId,
535                                   requestKey.type, requestKey.command);
536         return stdexec::set_stopped(std::move(receiver));
537     }
538 
539     /** @brief This function resets the stop callback. Validates the response
540      *         and sets either an error or a value on the receiver.
541      *
542      *  @param[in] eid - endpoint ID of the remote MCTP endpoint
543      *  @param[in] response - PLDM response message
544      *  @param[in] respMsgLen - length of the response message
545      *
546      *  @return PLDM completion code
547      */
onCompletepldm::requester::SendRecvMsgOperation548     void onComplete(mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen)
549     {
550         stopCallback.reset();
551         assert(eid == this->requestKey.eid);
552         auto rc = PLDM_SUCCESS;
553         if (!response && !respMsgLen)
554         {
555             rc = PLDM_ERROR_NOT_READY;
556         }
557         return stdexec::set_value(std::move(receiver), static_cast<int>(rc),
558                                   response, respMsgLen);
559     }
560 
561   private:
562     /** @brief Reference to a Handler object that manages the request/response
563      *         logic.
564      */
565     requester::Handler<RequestInterface>& handler;
566 
567     /** @brief Stores information about the request such as eid, instanceId,
568      *         type, and command.
569      */
570     RequestKey requestKey;
571 
572     /** @brief The request message to be sent.
573      */
574     pldm::Request request;
575 
576     /** @brief The response message for the sent request message.
577      */
578     const pldm_msg* response;
579 
580     /** @brief The length of response message for the sent request message.
581      */
582     size_t respMsgLen;
583 
584     /** @brief The receiver to be notified with the result of the operation.
585      */
586     R receiver;
587 
588     /** @brief An optional callback that handles stopping the operation if
589      *         requested.
590      */
591     std::optional<typename stdexec::stop_token_of_t<
592         stdexec::env_of_t<R>>::template callback_type<std::function<void()>>>
593         stopCallback = std::nullopt;
594 };
595 
596 /** @class SendRecvMsgSender
597  *
598  *  Represents the single message sender
599  *
600  * @tparam RequestInterface - Request class type
601  */
602 template <class RequestInterface>
603 struct SendRecvMsgSender
604 {
605     using is_sender = void;
606 
607     SendRecvMsgSender() = delete;
608 
SendRecvMsgSenderpldm::requester::SendRecvMsgSender609     explicit SendRecvMsgSender(requester::Handler<RequestInterface>& handler,
610                                mctp_eid_t eid, pldm::Request&& request) :
611         handler(handler), eid(eid), request(std::move(request))
612     {}
613 
614     friend auto tag_invoke(stdexec::get_completion_signatures_t,
615                            const SendRecvMsgSender&, auto)
616         -> stdexec::completion_signatures<
617             stdexec::set_value_t(int, const pldm_msg*, size_t),
618             stdexec::set_stopped_t()>;
619 
620     /** @brief Execute the sending the request message */
621     template <stdexec::receiver R>
tag_invoke(stdexec::connect_t,SendRecvMsgSender && self,R r)622     friend auto tag_invoke(stdexec::connect_t, SendRecvMsgSender&& self, R r)
623     {
624         return SendRecvMsgOperation<RequestInterface, R>(
625             self.handler, self.eid, std::move(self.request), std::move(r));
626     }
627 
628   private:
629     /** @brief Reference to a Handler object that manages the request/response
630      *         logic.
631      */
632     requester::Handler<RequestInterface>& handler;
633 
634     /** @brief MCTP Endpoint ID of request message */
635     mctp_eid_t eid;
636 
637     /** @brief Request message */
638     pldm::Request request;
639 };
640 
641 /** @brief Wrap registerRequest with coroutine API.
642  *
643  *  @param[in] eid - endpoint ID of the remote MCTP endpoint
644  *  @param[in] request - PLDM request message
645  *
646  *  @return Return [PLDM_ERROR, _, _] if registerRequest fails.
647  *          Return [PLDM_ERROR_NOT_READY, nullptr, 0] if timed out.
648  *          Return [PLDM_SUCCESS, resp, len] if succeeded
649  */
650 template <class RequestInterface>
651 stdexec::sender_of<stdexec::set_value_t(SendRecvCoResp)> auto
sendRecvMsg(mctp_eid_t eid,pldm::Request && request)652     Handler<RequestInterface>::sendRecvMsg(mctp_eid_t eid,
653                                            pldm::Request&& request)
654 {
655     return SendRecvMsgSender(*this, eid, std::move(request)) |
656            stdexec::then([](int rc, const pldm_msg* resp, size_t respLen) {
657                return std::make_tuple(rc, resp, respLen);
658            });
659 }
660 
661 } // namespace requester
662 
663 } // namespace pldm
664