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