xref: /openbmc/pldm/requester/handler.hpp (revision 4bf3ed840f13c31720ffa7c255942f3836402f4f)
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         else
402         {
403             // Got a response for a PLDM request message not registered with the
404             // request handler, so freeing up the instance ID, this can be other
405             // OpenBMC applications relying on PLDM D-Bus apis like
406             // openpower-occ-control and softoff
407             instanceIdDb.free(key.eid, key.instanceId);
408         }
409     }
410 
411     /** @brief Wrap registerRequest with coroutine API.
412      *
413      *  @return Return [PLDM_ERROR, _, _] if registerRequest fails.
414      *          Return [PLDM_ERROR_NOT_READY, nullptr, 0] if timed out.
415      *          Return [PLDM_SUCCESS, resp, len] if succeeded
416      */
417     stdexec::sender_of<stdexec::set_value_t(SendRecvCoResp)> auto sendRecvMsg(
418         mctp_eid_t eid, pldm::Request&& request);
419 
420   private:
421     PldmTransport* pldmTransport; //!< PLDM transport object
422     sdeventplus::Event& event; //!< reference to PLDM daemon's main event loop
423     pldm::InstanceIdDb& instanceIdDb; //!< reference to an InstanceIdDb
424     bool verbose;                     //!< verbose tracing flag
425     std::chrono::seconds
426         instanceIdExpiryInterval;     //!< Instance ID expiration interval
427     uint8_t numRetries;               //!< number of request retries
428     std::chrono::milliseconds
429         responseTimeOut;              //!< time to wait between each retry
430 
431     /** @brief Container for storing the details of the PLDM request
432      *         message, handler for the corresponding PLDM response and the
433      *         timer object for the Instance ID expiration
434      */
435     using RequestValue =
436         std::tuple<std::unique_ptr<RequestInterface>, ResponseHandler,
437                    std::unique_ptr<sdbusplus::Timer>>;
438 
439     // Manage the requests of responders base on MCTP EID
440     std::map<mctp_eid_t, std::shared_ptr<EndpointMessageQueue>>
441         endpointMessageQueues;
442 
443     /** @brief Container for storing the PLDM request entries */
444     std::unordered_map<RequestKey, RequestValue, RequestKeyHasher> handlers;
445 
446     /** @brief Container to store information about the request entries to be
447      *         removed after the instance ID timer expires
448      */
449     std::unordered_map<RequestKey, std::unique_ptr<sdeventplus::source::Defer>,
450                        RequestKeyHasher>
451         removeRequestContainer;
452 
453     /** @brief Remove request entry for which the instance ID expired
454      *
455      *  @param[in] key - key for the Request
456      */
removeRequestEntry(RequestKey key)457     void removeRequestEntry(RequestKey key)
458     {
459         if (removeRequestContainer.contains(key))
460         {
461             removeRequestContainer[key].reset();
462             instanceIdDb.free(key.eid, key.instanceId);
463             handlers.erase(key);
464             removeRequestContainer.erase(key);
465         }
466     }
467 };
468 
469 /** @class SendRecvMsgOperation
470  *
471  *  Represents the state and logic for a single send/receive message operation
472  *
473  * @tparam RequestInterface - Request class type
474  * @tparam stdexec::receiver - Execute receiver
475  */
476 template <class RequestInterface, stdexec::receiver R>
477 struct SendRecvMsgOperation
478 {
479     SendRecvMsgOperation() = delete;
480 
SendRecvMsgOperationpldm::requester::SendRecvMsgOperation481     explicit SendRecvMsgOperation(Handler<RequestInterface>& handler,
482                                   mctp_eid_t eid, pldm::Request&& request,
483                                   R&& r) :
484         handler(handler), request(std::move(request)), receiver(std::move(r))
485     {
486         auto requestMsg =
487             reinterpret_cast<const pldm_msg*>(this->request.data());
488         requestKey = RequestKey{
489             eid,
490             requestMsg->hdr.instance_id,
491             requestMsg->hdr.type,
492             requestMsg->hdr.command,
493         };
494         response = nullptr;
495         respMsgLen = 0;
496     }
497 
498     /** @brief Checks if the operation has been requested to stop.
499      *         If so, it sets the state to stopped.Registers the request with
500      *         the handler. If registration fails, sets an error on the
501      *         receiver. If stopping is possible, sets up a stop callback.
502      *
503      *  @param[in] op - operation request
504      *
505      *  @return Execute errors
506      */
tag_invoke(stdexec::start_t,SendRecvMsgOperation & op)507     friend void tag_invoke(stdexec::start_t, SendRecvMsgOperation& op) noexcept
508     {
509         auto stopToken = stdexec::get_stop_token(stdexec::get_env(op.receiver));
510 
511         // operation already cancelled
512         if (stopToken.stop_requested())
513         {
514             return stdexec::set_stopped(std::move(op.receiver));
515         }
516 
517         using namespace std::placeholders;
518         auto rc = op.handler.registerRequest(
519             op.requestKey.eid, op.requestKey.instanceId, op.requestKey.type,
520             op.requestKey.command, std::move(op.request),
521             std::bind(&SendRecvMsgOperation::onComplete, &op, _1, _2, _3));
522         if (rc)
523         {
524             return stdexec::set_value(std::move(op.receiver), rc,
525                                       static_cast<const pldm_msg*>(nullptr),
526                                       static_cast<size_t>(0));
527         }
528 
529         if (stopToken.stop_possible())
530         {
531             op.stopCallback.emplace(
532                 std::move(stopToken),
533                 std::bind(&SendRecvMsgOperation::onStop, &op));
534         }
535     }
536 
537     /** @brief Unregisters the request and sets the state to stopped on the
538      *         receiver.
539      */
onStoppldm::requester::SendRecvMsgOperation540     void onStop()
541     {
542         handler.unregisterRequest(requestKey.eid, requestKey.instanceId,
543                                   requestKey.type, requestKey.command);
544         return stdexec::set_stopped(std::move(receiver));
545     }
546 
547     /** @brief This function resets the stop callback. Validates the response
548      *         and sets either an error or a value on the receiver.
549      *
550      *  @param[in] eid - endpoint ID of the remote MCTP endpoint
551      *  @param[in] response - PLDM response message
552      *  @param[in] respMsgLen - length of the response message
553      *
554      *  @return PLDM completion code
555      */
onCompletepldm::requester::SendRecvMsgOperation556     void onComplete(mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen)
557     {
558         stopCallback.reset();
559         assert(eid == this->requestKey.eid);
560         auto rc = PLDM_SUCCESS;
561         if (!response && !respMsgLen)
562         {
563             rc = PLDM_ERROR_NOT_READY;
564         }
565         return stdexec::set_value(std::move(receiver), static_cast<int>(rc),
566                                   response, respMsgLen);
567     }
568 
569   private:
570     /** @brief Reference to a Handler object that manages the request/response
571      *         logic.
572      */
573     requester::Handler<RequestInterface>& handler;
574 
575     /** @brief Stores information about the request such as eid, instanceId,
576      *         type, and command.
577      */
578     RequestKey requestKey;
579 
580     /** @brief The request message to be sent.
581      */
582     pldm::Request request;
583 
584     /** @brief The response message for the sent request message.
585      */
586     const pldm_msg* response;
587 
588     /** @brief The length of response message for the sent request message.
589      */
590     size_t respMsgLen;
591 
592     /** @brief The receiver to be notified with the result of the operation.
593      */
594     R receiver;
595 
596     /** @brief An optional callback that handles stopping the operation if
597      *         requested.
598      */
599     std::optional<typename stdexec::stop_token_of_t<
600         stdexec::env_of_t<R>>::template callback_type<std::function<void()>>>
601         stopCallback = std::nullopt;
602 };
603 
604 /** @class SendRecvMsgSender
605  *
606  *  Represents the single message sender
607  *
608  * @tparam RequestInterface - Request class type
609  */
610 template <class RequestInterface>
611 struct SendRecvMsgSender
612 {
613     using is_sender = void;
614 
615     SendRecvMsgSender() = delete;
616 
SendRecvMsgSenderpldm::requester::SendRecvMsgSender617     explicit SendRecvMsgSender(requester::Handler<RequestInterface>& handler,
618                                mctp_eid_t eid, pldm::Request&& request) :
619         handler(handler), eid(eid), request(std::move(request))
620     {}
621 
622     friend auto tag_invoke(stdexec::get_completion_signatures_t,
623                            const SendRecvMsgSender&, auto)
624         -> stdexec::completion_signatures<
625             stdexec::set_value_t(int, const pldm_msg*, size_t),
626             stdexec::set_stopped_t()>;
627 
628     /** @brief Execute the sending the request message */
629     template <stdexec::receiver R>
tag_invoke(stdexec::connect_t,SendRecvMsgSender && self,R r)630     friend auto tag_invoke(stdexec::connect_t, SendRecvMsgSender&& self, R r)
631     {
632         return SendRecvMsgOperation<RequestInterface, R>(
633             self.handler, self.eid, std::move(self.request), std::move(r));
634     }
635 
636   private:
637     /** @brief Reference to a Handler object that manages the request/response
638      *         logic.
639      */
640     requester::Handler<RequestInterface>& handler;
641 
642     /** @brief MCTP Endpoint ID of request message */
643     mctp_eid_t eid;
644 
645     /** @brief Request message */
646     pldm::Request request;
647 };
648 
649 /** @brief Wrap registerRequest with coroutine API.
650  *
651  *  @param[in] eid - endpoint ID of the remote MCTP endpoint
652  *  @param[in] request - PLDM request message
653  *
654  *  @return Return [PLDM_ERROR, _, _] if registerRequest fails.
655  *          Return [PLDM_ERROR_NOT_READY, nullptr, 0] if timed out.
656  *          Return [PLDM_SUCCESS, resp, len] if succeeded
657  */
658 template <class RequestInterface>
659 stdexec::sender_of<stdexec::set_value_t(SendRecvCoResp)> auto
sendRecvMsg(mctp_eid_t eid,pldm::Request && request)660     Handler<RequestInterface>::sendRecvMsg(mctp_eid_t eid,
661                                            pldm::Request&& request)
662 {
663     return SendRecvMsgSender(*this, eid, std::move(request)) |
664            stdexec::then([](int rc, const pldm_msg* resp, size_t respLen) {
665                return std::make_tuple(rc, resp, respLen);
666            });
667 }
668 
669 } // namespace requester
670 
671 } // namespace pldm
672