blob: cefeb7860217fd7dbccb737a79439ed983226b96 [file] [log] [blame]
Tom Joseph74f27c72021-05-16 07:58:53 -07001#pragma once
2
Andrew Jeffery2abbce72023-10-18 10:17:35 +10303#include "common/instance_id.hpp"
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +10004#include "common/transport.hpp"
Tom Joseph74f27c72021-05-16 07:58:53 -07005#include "common/types.hpp"
Tom Joseph74f27c72021-05-16 07:58:53 -07006#include "request.hpp"
7
George Liuc453e162022-12-21 17:16:23 +08008#include <libpldm/base.h>
Manojkiran Eda9fffea22021-10-27 16:03:27 +05309#include <sys/socket.h>
10
Riya Dixit49cfb132023-03-02 04:26:53 -060011#include <phosphor-logging/lg2.hpp>
Gilbert Chena85c69d2024-01-09 21:25:26 +070012#include <sdbusplus/async.hpp>
Tom Joseph74f27c72021-05-16 07:58:53 -070013#include <sdbusplus/timer.hpp>
14#include <sdeventplus/event.hpp>
15#include <sdeventplus/source/event.hpp>
16
17#include <cassert>
18#include <chrono>
Thu Nguyen4ddee3a2023-08-03 08:20:03 +070019#include <deque>
Patrick Williamscdbb9e22023-07-13 18:05:40 -050020#include <functional>
Tom Joseph74f27c72021-05-16 07:58:53 -070021#include <memory>
Thu Nguyen4ddee3a2023-08-03 08:20:03 +070022#include <mutex>
23#include <queue>
Tom Joseph74f27c72021-05-16 07:58:53 -070024#include <tuple>
25#include <unordered_map>
26
Riya Dixit49cfb132023-03-02 04:26:53 -060027PHOSPHOR_LOG2_USING;
28
Tom Joseph74f27c72021-05-16 07:58:53 -070029namespace pldm
30{
Tom Joseph74f27c72021-05-16 07:58:53 -070031namespace requester
32{
Tom Joseph74f27c72021-05-16 07:58:53 -070033/** @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 */
39struct 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
46 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 */
58struct RequestKeyHasher
59{
60 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
Thu Nguyen4ddee3a2023-08-03 08:20:03 +070067using ResponseHandler = std::function<void(
Tom Joseph74f27c72021-05-16 07:58:53 -070068 mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen)>;
Tom Joseph74f27c72021-05-16 07:58:53 -070069
Thu Nguyen4ddee3a2023-08-03 08:20:03 +070070/** @struct RegisteredRequest
71 *
72 * This struct is used to store the registered request to one endpoint.
73 */
74struct RegisteredRequest
75{
76 RequestKey key; //!< Responder MCTP endpoint ID
77 std::vector<uint8_t> reqMsg; //!< Request messages queue
78 ResponseHandler responseHandler; //!< Waiting for response flag
79};
80
81/** @struct EndpointMessageQueue
82 *
83 * This struct is used to save the list of request messages of one endpoint and
84 * the existing of the request message to the endpoint with its' EID.
85 */
86struct EndpointMessageQueue
87{
88 mctp_eid_t eid; //!< Responder MCTP endpoint ID
89 std::deque<std::shared_ptr<RegisteredRequest>> requestQueue; //!< Queue
90 bool activeRequest; //!< Waiting for response flag
91
92 bool operator==(const mctp_eid_t& mctpEid) const
93 {
94 return (eid == mctpEid);
95 }
96};
97
Tom Joseph74f27c72021-05-16 07:58:53 -070098/** @class Handler
99 *
100 * This class handles the lifecycle of the PLDM request message based on the
101 * instance ID expiration interval, number of request retries and the timeout
102 * waiting for a response. The registered response handlers are invoked with
103 * response once the PLDM responder sends the response. If no response is
104 * received within the instance ID expiration interval or any other failure the
105 * response handler is invoked with the empty response.
106 *
107 * @tparam RequestInterface - Request class type
108 */
109template <class RequestInterface>
110class Handler
111{
Tom Joseph74f27c72021-05-16 07:58:53 -0700112 public:
113 Handler() = delete;
114 Handler(const Handler&) = delete;
115 Handler(Handler&&) = delete;
116 Handler& operator=(const Handler&) = delete;
117 Handler& operator=(Handler&&) = delete;
118 ~Handler() = default;
119
120 /** @brief Constructor
121 *
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +1000122 * @param[in] pldm_transport - PLDM requester
Tom Joseph74f27c72021-05-16 07:58:53 -0700123 * @param[in] event - reference to PLDM daemon's main event loop
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930124 * @param[in] instanceIdDb - reference to an InstanceIdDb
Tom Josephe5268cd2021-09-07 13:04:03 +0530125 * @param[in] verbose - verbose tracing flag
Tom Joseph74f27c72021-05-16 07:58:53 -0700126 * @param[in] instanceIdExpiryInterval - instance ID expiration interval
127 * @param[in] numRetries - number of request retries
128 * @param[in] responseTimeOut - time to wait between each retry
129 */
130 explicit Handler(
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +1000131 PldmTransport* pldmTransport, sdeventplus::Event& event,
132 pldm::InstanceIdDb& instanceIdDb, bool verbose,
Brad Bishop5079ac42021-08-19 18:35:06 -0400133 std::chrono::seconds instanceIdExpiryInterval =
134 std::chrono::seconds(INSTANCE_ID_EXPIRATION_INTERVAL),
Tom Joseph74f27c72021-05-16 07:58:53 -0700135 uint8_t numRetries = static_cast<uint8_t>(NUMBER_OF_REQUEST_RETRIES),
Brad Bishop5079ac42021-08-19 18:35:06 -0400136 std::chrono::milliseconds responseTimeOut =
137 std::chrono::milliseconds(RESPONSE_TIME_OUT)) :
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +1000138 pldmTransport(pldmTransport),
139 event(event), instanceIdDb(instanceIdDb), verbose(verbose),
Tom Joseph74f27c72021-05-16 07:58:53 -0700140 instanceIdExpiryInterval(instanceIdExpiryInterval),
141 numRetries(numRetries), responseTimeOut(responseTimeOut)
142 {}
143
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700144 void instanceIdExpiryCallBack(RequestKey key)
145 {
146 auto eid = key.eid;
147 if (this->handlers.contains(key))
148 {
Riya Dixit087a7512024-04-06 14:28:08 -0500149 info(
150 "Instance ID expiry for EID '{EID}' using InstanceID '{INSTANCEID}'",
151 "EID", (unsigned)key.eid, "INSTANCEID",
152 (unsigned)key.instanceId);
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700153 auto& [request, responseHandler,
154 timerInstance] = this->handlers[key];
155 request->stop();
156 auto rc = timerInstance->stop();
157 if (rc)
158 {
Riya Dixit087a7512024-04-06 14:28:08 -0500159 error(
160 "Failed to stop the instance ID expiry timer, response code '{RC}'",
161 "RC", static_cast<int>(rc));
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700162 }
163 // Call response handler with an empty response to indicate no
164 // response
165 responseHandler(eid, nullptr, 0);
166 this->removeRequestContainer.emplace(
167 key,
168 std::make_unique<sdeventplus::source::Defer>(
169 event, std::bind(&Handler::removeRequestEntry, this, key)));
170 endpointMessageQueues[eid]->activeRequest = false;
171
172 /* try to send new request if the endpoint is free */
173 pollEndpointQueue(eid);
174 }
175 else
176 {
177 // This condition is not possible, if a response is received
178 // before the instance ID expiry, then the response handler
179 // is executed and the entry will be removed.
180 assert(false);
181 }
182 }
183
184 /** @brief Send the remaining PLDM request messages in endpoint queue
185 *
186 * @param[in] eid - endpoint ID of the remote MCTP endpoint
187 */
188 int pollEndpointQueue(mctp_eid_t eid)
189 {
190 if (endpointMessageQueues[eid]->activeRequest ||
191 endpointMessageQueues[eid]->requestQueue.empty())
192 {
193 return PLDM_SUCCESS;
194 }
195
196 endpointMessageQueues[eid]->activeRequest = true;
197 auto requestMsg = endpointMessageQueues[eid]->requestQueue.front();
198 endpointMessageQueues[eid]->requestQueue.pop_front();
199
200 auto request = std::make_unique<RequestInterface>(
201 pldmTransport, requestMsg->key.eid, event,
202 std::move(requestMsg->reqMsg), numRetries, responseTimeOut,
203 verbose);
Patrick Williams35535cf2023-12-05 12:45:02 -0600204 auto timer = std::make_unique<sdbusplus::Timer>(
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700205 event.get(), std::bind(&Handler::instanceIdExpiryCallBack, this,
206 requestMsg->key));
207
208 auto rc = request->start();
209 if (rc)
210 {
211 instanceIdDb.free(requestMsg->key.eid, requestMsg->key.instanceId);
Riya Dixit087a7512024-04-06 14:28:08 -0500212 error(
213 "Failure to send the PLDM request message for polling endpoint queue, response code '{RC}'",
214 "RC", rc);
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700215 endpointMessageQueues[eid]->activeRequest = false;
216 return rc;
217 }
218
219 try
220 {
221 timer->start(duration_cast<std::chrono::microseconds>(
222 instanceIdExpiryInterval));
223 }
224 catch (const std::runtime_error& e)
225 {
226 instanceIdDb.free(requestMsg->key.eid, requestMsg->key.instanceId);
227 error(
Riya Dixit087a7512024-04-06 14:28:08 -0500228 "Failed to start the instance ID expiry timer, error - {ERROR}",
229 "ERROR", e);
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700230 endpointMessageQueues[eid]->activeRequest = false;
231 return PLDM_ERROR;
232 }
233
234 handlers.emplace(requestMsg->key,
235 std::make_tuple(std::move(request),
236 std::move(requestMsg->responseHandler),
237 std::move(timer)));
238 return PLDM_SUCCESS;
239 }
240
Tom Joseph74f27c72021-05-16 07:58:53 -0700241 /** @brief Register a PLDM request message
242 *
243 * @param[in] eid - endpoint ID of the remote MCTP endpoint
244 * @param[in] instanceId - instance ID to match request and response
245 * @param[in] type - PLDM type
246 * @param[in] command - PLDM command
247 * @param[in] requestMsg - PLDM request message
248 * @param[in] responseHandler - Response handler for this request
249 *
250 * @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise
251 */
252 int registerRequest(mctp_eid_t eid, uint8_t instanceId, uint8_t type,
253 uint8_t command, pldm::Request&& requestMsg,
254 ResponseHandler&& responseHandler)
255 {
256 RequestKey key{eid, instanceId, type, command};
257
Thu Nguyenb4e8cfd2023-03-23 15:06:44 +0700258 if (handlers.contains(key))
259 {
Riya Dixit087a7512024-04-06 14:28:08 -0500260 error(
261 "Register request for EID '{EID}' is using InstanceID '{INSTANCEID}'",
262 "EID", (unsigned)eid, "INSTANCEID", (unsigned)instanceId);
Thu Nguyenb4e8cfd2023-03-23 15:06:44 +0700263 return PLDM_ERROR;
264 }
265
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700266 auto inputRequest = std::make_shared<RegisteredRequest>(
267 key, std::move(requestMsg), std::move(responseHandler));
268 if (endpointMessageQueues.contains(eid))
Tom Joseph74f27c72021-05-16 07:58:53 -0700269 {
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700270 endpointMessageQueues[eid]->requestQueue.push_back(inputRequest);
271 }
272 else
273 {
274 std::deque<std::shared_ptr<RegisteredRequest>> reqQueue;
275 reqQueue.push_back(inputRequest);
276 endpointMessageQueues[eid] =
277 std::make_shared<EndpointMessageQueue>(eid, reqQueue, false);
Tom Joseph74f27c72021-05-16 07:58:53 -0700278 }
279
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700280 /* try to send new request if the endpoint is free */
281 pollEndpointQueue(eid);
Tom Joseph74f27c72021-05-16 07:58:53 -0700282
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700283 return PLDM_SUCCESS;
Tom Joseph74f27c72021-05-16 07:58:53 -0700284 }
285
Gilbert Chena85c69d2024-01-09 21:25:26 +0700286 /** @brief Unregister a PLDM request message
287 *
288 * @param[in] eid - endpoint ID of the remote MCTP endpoint
289 * @param[in] instanceId - instance ID to match request and response
290 * @param[in] type - PLDM type
291 * @param[in] command - PLDM command
292 *
293 * @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise
294 */
295 int unregisterRequest(mctp_eid_t eid, uint8_t instanceId, uint8_t type,
296 uint8_t command)
297 {
298 RequestKey key{eid, instanceId, type, command};
299
300 /* handlers only contain key when the message is already sent */
301 if (handlers.contains(key))
302 {
303 auto& [request, responseHandler, timerInstance] = handlers[key];
304 request->stop();
305 auto rc = timerInstance->stop();
306 if (rc)
307 {
308 error(
309 "Failed to stop the instance ID expiry timer, response code '{RC}'",
310 "RC", static_cast<int>(rc));
311 }
312
313 instanceIdDb.free(key.eid, key.instanceId);
314 handlers.erase(key);
315 endpointMessageQueues[eid]->activeRequest = false;
316 /* try to send new request if the endpoint is free */
317 pollEndpointQueue(eid);
318
319 return PLDM_SUCCESS;
320 }
321 else
322 {
323 if (!endpointMessageQueues.contains(eid))
324 {
325 error(
326 "Can't find request for EID '{EID}' is using InstanceID '{INSTANCEID}' in Endpoint message Queue",
327 "EID", (unsigned)eid, "INSTANCEID", (unsigned)instanceId);
328 return PLDM_ERROR;
329 }
330 auto requestMsg = endpointMessageQueues[eid]->requestQueue;
331 /* Find the registered request in the requestQueue */
332 for (auto it = requestMsg.begin(); it != requestMsg.end();)
333 {
334 auto msg = *it;
335 if (msg->key == key)
336 {
337 // erase and get the next valid iterator
338 it = endpointMessageQueues[eid]->requestQueue.erase(it);
339 instanceIdDb.free(key.eid, key.instanceId);
340 return PLDM_SUCCESS;
341 }
342 else
343 {
344 ++it; // increment iterator only if not erasing
345 }
346 }
347 }
348
349 return PLDM_ERROR;
350 }
351
Tom Joseph74f27c72021-05-16 07:58:53 -0700352 /** @brief Handle PLDM response message
353 *
354 * @param[in] eid - endpoint ID of the remote MCTP endpoint
355 * @param[in] instanceId - instance ID to match request and response
356 * @param[in] type - PLDM type
357 * @param[in] command - PLDM command
358 * @param[in] response - PLDM response message
359 * @param[in] respMsgLen - length of the response message
360 */
361 void handleResponse(mctp_eid_t eid, uint8_t instanceId, uint8_t type,
362 uint8_t command, const pldm_msg* response,
363 size_t respMsgLen)
364 {
365 RequestKey key{eid, instanceId, type, command};
366 if (handlers.contains(key))
367 {
368 auto& [request, responseHandler, timerInstance] = handlers[key];
369 request->stop();
370 auto rc = timerInstance->stop();
371 if (rc)
372 {
Riya Dixit087a7512024-04-06 14:28:08 -0500373 error(
374 "Failed to stop the instance ID expiry timer, response code '{RC}'",
375 "RC", static_cast<int>(rc));
Tom Joseph74f27c72021-05-16 07:58:53 -0700376 }
377 responseHandler(eid, response, respMsgLen);
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930378 instanceIdDb.free(key.eid, key.instanceId);
Tom Joseph74f27c72021-05-16 07:58:53 -0700379 handlers.erase(key);
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700380
381 endpointMessageQueues[eid]->activeRequest = false;
382 /* try to send new request if the endpoint is free */
383 pollEndpointQueue(eid);
Tom Joseph74f27c72021-05-16 07:58:53 -0700384 }
385 else
386 {
387 // Got a response for a PLDM request message not registered with the
388 // request handler, so freeing up the instance ID, this can be other
389 // OpenBMC applications relying on PLDM D-Bus apis like
390 // openpower-occ-control and softoff
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930391 instanceIdDb.free(key.eid, key.instanceId);
Tom Joseph74f27c72021-05-16 07:58:53 -0700392 }
393 }
394
Gilbert Chena85c69d2024-01-09 21:25:26 +0700395 /** @brief Wrap registerRequest with coroutine API.
396 *
397 * @return A tuple of [return_code, pldm::Response].
398 * pldm::Response is empty on non-zero return_code.
399 * Otherwise, filled with pldm_msg* content.
400 */
401 stdexec::sender auto sendRecvMsg(mctp_eid_t eid, pldm::Request&& request);
402
Tom Joseph74f27c72021-05-16 07:58:53 -0700403 private:
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +1000404 PldmTransport* pldmTransport; //!< PLDM transport object
Brad Bishop5079ac42021-08-19 18:35:06 -0400405 sdeventplus::Event& event; //!< reference to PLDM daemon's main event loop
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930406 pldm::InstanceIdDb& instanceIdDb; //!< reference to an InstanceIdDb
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930407 bool verbose; //!< verbose tracing flag
Brad Bishop5079ac42021-08-19 18:35:06 -0400408 std::chrono::seconds
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930409 instanceIdExpiryInterval; //!< Instance ID expiration interval
410 uint8_t numRetries; //!< number of request retries
Brad Bishop5079ac42021-08-19 18:35:06 -0400411 std::chrono::milliseconds
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930412 responseTimeOut; //!< time to wait between each retry
Tom Joseph74f27c72021-05-16 07:58:53 -0700413
Tom Josephe5268cd2021-09-07 13:04:03 +0530414 /** @brief Container for storing the details of the PLDM request
415 * message, handler for the corresponding PLDM response and the
416 * timer object for the Instance ID expiration
Tom Joseph74f27c72021-05-16 07:58:53 -0700417 */
Brad Bishop5079ac42021-08-19 18:35:06 -0400418 using RequestValue =
419 std::tuple<std::unique_ptr<RequestInterface>, ResponseHandler,
Patrick Williams35535cf2023-12-05 12:45:02 -0600420 std::unique_ptr<sdbusplus::Timer>>;
Tom Joseph74f27c72021-05-16 07:58:53 -0700421
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700422 // Manage the requests of responders base on MCTP EID
423 std::map<mctp_eid_t, std::shared_ptr<EndpointMessageQueue>>
424 endpointMessageQueues;
425
Tom Joseph74f27c72021-05-16 07:58:53 -0700426 /** @brief Container for storing the PLDM request entries */
427 std::unordered_map<RequestKey, RequestValue, RequestKeyHasher> handlers;
428
429 /** @brief Container to store information about the request entries to be
430 * removed after the instance ID timer expires
431 */
Brad Bishop5079ac42021-08-19 18:35:06 -0400432 std::unordered_map<RequestKey, std::unique_ptr<sdeventplus::source::Defer>,
433 RequestKeyHasher>
Tom Joseph74f27c72021-05-16 07:58:53 -0700434 removeRequestContainer;
435
436 /** @brief Remove request entry for which the instance ID expired
437 *
438 * @param[in] key - key for the Request
439 */
440 void removeRequestEntry(RequestKey key)
441 {
442 if (removeRequestContainer.contains(key))
443 {
444 removeRequestContainer[key].reset();
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930445 instanceIdDb.free(key.eid, key.instanceId);
Tom Joseph74f27c72021-05-16 07:58:53 -0700446 handlers.erase(key);
447 removeRequestContainer.erase(key);
448 }
449 }
450};
451
Gilbert Chena85c69d2024-01-09 21:25:26 +0700452/** @class SendRecvMsgOperation
453 *
454 * Represents the state and logic for a single send/receive message operation
455 *
456 * @tparam RequestInterface - Request class type
457 * @tparam stdexec::receiver - Execute receiver
458 */
459template <class RequestInterface, stdexec::receiver R>
460struct SendRecvMsgOperation
461{
462 SendRecvMsgOperation() = delete;
463
464 explicit SendRecvMsgOperation(Handler<RequestInterface>& handler,
465 mctp_eid_t eid, pldm::Request&& request,
466 R&& r) :
467 handler(handler),
468 request(std::move(request)), receiver(std::move(r))
469 {
470 auto requestMsg =
471 reinterpret_cast<const pldm_msg*>(this->request.data());
472 requestKey = RequestKey{
473 eid,
474 requestMsg->hdr.instance_id,
475 requestMsg->hdr.type,
476 requestMsg->hdr.command,
477 };
478 response = nullptr;
479 respMsgLen = 0;
480 }
481
482 /** @brief Checks if the operation has been requested to stop.
483 * If so, it sets the state to stopped.Registers the request with
484 * the handler. If registration fails, sets an error on the
485 * receiver. If stopping is possible, sets up a stop callback.
486 *
487 * @param[in] op - operation request
488 *
489 * @return Execute errors
490 */
491 friend void tag_invoke(stdexec::start_t, SendRecvMsgOperation& op) noexcept
492 {
493 auto stopToken = stdexec::get_stop_token(stdexec::get_env(op.receiver));
494
495 // operation already cancelled
496 if (stopToken.stop_requested())
497 {
498 return stdexec::set_stopped(std::move(op.receiver));
499 }
500
501 using namespace std::placeholders;
502 auto rc = op.handler.registerRequest(
503 op.requestKey.eid, op.requestKey.instanceId, op.requestKey.type,
504 op.requestKey.command, std::move(op.request),
505 std::bind(&SendRecvMsgOperation::onComplete, &op, _1, _2, _3));
506 if (rc)
507 {
508 return stdexec::set_error(std::move(op.receiver), rc);
509 }
510
511 if (stopToken.stop_possible())
512 {
513 op.stopCallback.emplace(
514 std::move(stopToken),
515 std::bind(&SendRecvMsgOperation::onStop, &op));
516 }
517 }
518
519 /** @brief Unregisters the request and sets the state to stopped on the
520 * receiver.
521 */
522 void onStop()
523 {
524 handler.unregisterRequest(requestKey.eid, requestKey.instanceId,
525 requestKey.type, requestKey.command);
526 return stdexec::set_stopped(std::move(receiver));
527 }
528
529 /** @brief This function resets the stop callback. Validates the response
530 * and sets either an error or a value on the receiver.
531 *
532 * @param[in] eid - endpoint ID of the remote MCTP endpoint
533 * @param[in] response - PLDM response message
534 * @param[in] respMsgLen - length of the response message
535 *
536 * @return PLDM completion code
537 */
538 void onComplete(mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen)
539 {
540 stopCallback.reset();
541 assert(eid == this->requestKey.eid);
542 if (!response || !respMsgLen)
543 {
544 return stdexec::set_error(std::move(receiver),
545 static_cast<int>(PLDM_ERROR));
546 }
547 else
548 {
549 return stdexec::set_value(std::move(receiver), response,
550 respMsgLen);
551 }
552 }
553
554 private:
555 /** @brief Reference to a Handler object that manages the request/response
556 * logic.
557 */
558 requester::Handler<RequestInterface>& handler;
559
560 /** @brief Stores information about the request such as eid, instanceId,
561 * type, and command.
562 */
563 RequestKey requestKey;
564
565 /** @brief The request message to be sent.
566 */
567 pldm::Request request;
568
569 /** @brief The response message for the sent request message.
570 */
571 const pldm_msg* response;
572
573 /** @brief The length of response message for the sent request message.
574 */
575 size_t respMsgLen;
576
577 /** @brief The receiver to be notified with the result of the operation.
578 */
579 R receiver;
580
581 /** @brief An optional callback that handles stopping the operation if
582 * requested.
583 */
584 std::optional<typename stdexec::stop_token_of_t<
585 stdexec::env_of_t<R>>::template callback_type<std::function<void()>>>
586 stopCallback = std::nullopt;
587};
588
589/** @class SendRecvMsgSender
590 *
591 * Represents the single message sender
592 *
593 * @tparam RequestInterface - Request class type
594 */
595template <class RequestInterface>
596struct SendRecvMsgSender
597{
598 using is_sender = void;
599
600 SendRecvMsgSender() = delete;
601
602 explicit SendRecvMsgSender(requester::Handler<RequestInterface>& handler,
603 mctp_eid_t eid, pldm::Request&& request) :
604 handler(handler),
605 eid(eid), request(std::move(request))
606 {}
607
608 friend auto tag_invoke(stdexec::get_completion_signatures_t,
609 const SendRecvMsgSender&, auto)
610 -> stdexec::completion_signatures<
611 stdexec::set_value_t(const pldm_msg*, size_t),
612 stdexec::set_error_t(int), stdexec::set_stopped_t()>;
613
614 /** @brief Execute the sending the request message */
615 template <stdexec::receiver R>
616 friend auto tag_invoke(stdexec::connect_t, SendRecvMsgSender&& self, R r)
617 {
618 return SendRecvMsgOperation<RequestInterface, R>(
619 self.handler, self.eid, std::move(self.request), std::move(r));
620 }
621
622 private:
623 /** @brief Reference to a Handler object that manages the request/response
624 * logic.
625 */
626 requester::Handler<RequestInterface>& handler;
627
628 /** @brief MCTP Endpoint ID of request message */
629 mctp_eid_t eid;
630
631 /** @brief Request message */
632 pldm::Request request;
633};
634
635/** @brief This function handles sending the request message and responses the
636 * response message for the caller.
637 *
638 * @param[in] eid - endpoint ID of the remote MCTP endpoint
639 * @param[in] request - PLDM request message
640 *
641 * @return The reponse message and response message length.
642 */
643template <class RequestInterface>
644stdexec::sender auto
645 Handler<RequestInterface>::sendRecvMsg(mctp_eid_t eid,
646 pldm::Request&& request)
647{
648 return SendRecvMsgSender(*this, eid, std::move(request)) |
649 stdexec::then([](const pldm_msg* responseMsg, size_t respMsgLen) {
650 return std::make_tuple(responseMsg, respMsgLen);
651 });
652}
653
Tom Joseph74f27c72021-05-16 07:58:53 -0700654} // namespace requester
655
656} // namespace pldm