blob: 20da26cd57d9d00cf2dfd07843210ab297a0dd89 [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}'",
Riya Dixit1e5c81e2024-05-03 07:54:00 -0500151 "EID", key.eid, "INSTANCEID", key.instanceId);
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700152 auto& [request, responseHandler,
153 timerInstance] = this->handlers[key];
154 request->stop();
155 auto rc = timerInstance->stop();
156 if (rc)
157 {
Riya Dixit087a7512024-04-06 14:28:08 -0500158 error(
159 "Failed to stop the instance ID expiry timer, response code '{RC}'",
Riya Dixit1e5c81e2024-05-03 07:54:00 -0500160 "RC", rc);
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700161 }
162 // Call response handler with an empty response to indicate no
163 // response
164 responseHandler(eid, nullptr, 0);
165 this->removeRequestContainer.emplace(
166 key,
167 std::make_unique<sdeventplus::source::Defer>(
168 event, std::bind(&Handler::removeRequestEntry, this, key)));
169 endpointMessageQueues[eid]->activeRequest = false;
170
171 /* try to send new request if the endpoint is free */
172 pollEndpointQueue(eid);
173 }
174 else
175 {
176 // This condition is not possible, if a response is received
177 // before the instance ID expiry, then the response handler
178 // is executed and the entry will be removed.
179 assert(false);
180 }
181 }
182
183 /** @brief Send the remaining PLDM request messages in endpoint queue
184 *
185 * @param[in] eid - endpoint ID of the remote MCTP endpoint
186 */
187 int pollEndpointQueue(mctp_eid_t eid)
188 {
189 if (endpointMessageQueues[eid]->activeRequest ||
190 endpointMessageQueues[eid]->requestQueue.empty())
191 {
192 return PLDM_SUCCESS;
193 }
194
195 endpointMessageQueues[eid]->activeRequest = true;
196 auto requestMsg = endpointMessageQueues[eid]->requestQueue.front();
197 endpointMessageQueues[eid]->requestQueue.pop_front();
198
199 auto request = std::make_unique<RequestInterface>(
200 pldmTransport, requestMsg->key.eid, event,
201 std::move(requestMsg->reqMsg), numRetries, responseTimeOut,
202 verbose);
Patrick Williams35535cf2023-12-05 12:45:02 -0600203 auto timer = std::make_unique<sdbusplus::Timer>(
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700204 event.get(), std::bind(&Handler::instanceIdExpiryCallBack, this,
205 requestMsg->key));
206
207 auto rc = request->start();
208 if (rc)
209 {
210 instanceIdDb.free(requestMsg->key.eid, requestMsg->key.instanceId);
Riya Dixit087a7512024-04-06 14:28:08 -0500211 error(
212 "Failure to send the PLDM request message for polling endpoint queue, response code '{RC}'",
213 "RC", rc);
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700214 endpointMessageQueues[eid]->activeRequest = false;
215 return rc;
216 }
217
218 try
219 {
220 timer->start(duration_cast<std::chrono::microseconds>(
221 instanceIdExpiryInterval));
222 }
223 catch (const std::runtime_error& e)
224 {
225 instanceIdDb.free(requestMsg->key.eid, requestMsg->key.instanceId);
226 error(
Riya Dixit087a7512024-04-06 14:28:08 -0500227 "Failed to start the instance ID expiry timer, error - {ERROR}",
228 "ERROR", e);
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700229 endpointMessageQueues[eid]->activeRequest = false;
230 return PLDM_ERROR;
231 }
232
233 handlers.emplace(requestMsg->key,
234 std::make_tuple(std::move(request),
235 std::move(requestMsg->responseHandler),
236 std::move(timer)));
237 return PLDM_SUCCESS;
238 }
239
Tom Joseph74f27c72021-05-16 07:58:53 -0700240 /** @brief Register a PLDM request message
241 *
242 * @param[in] eid - endpoint ID of the remote MCTP endpoint
243 * @param[in] instanceId - instance ID to match request and response
244 * @param[in] type - PLDM type
245 * @param[in] command - PLDM command
246 * @param[in] requestMsg - PLDM request message
247 * @param[in] responseHandler - Response handler for this request
248 *
249 * @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise
250 */
251 int registerRequest(mctp_eid_t eid, uint8_t instanceId, uint8_t type,
252 uint8_t command, pldm::Request&& requestMsg,
253 ResponseHandler&& responseHandler)
254 {
255 RequestKey key{eid, instanceId, type, command};
256
Thu Nguyenb4e8cfd2023-03-23 15:06:44 +0700257 if (handlers.contains(key))
258 {
Riya Dixit087a7512024-04-06 14:28:08 -0500259 error(
260 "Register request for EID '{EID}' is using InstanceID '{INSTANCEID}'",
Riya Dixit1e5c81e2024-05-03 07:54:00 -0500261 "EID", eid, "INSTANCEID", instanceId);
Thu Nguyenb4e8cfd2023-03-23 15:06:44 +0700262 return PLDM_ERROR;
263 }
264
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700265 auto inputRequest = std::make_shared<RegisteredRequest>(
266 key, std::move(requestMsg), std::move(responseHandler));
267 if (endpointMessageQueues.contains(eid))
Tom Joseph74f27c72021-05-16 07:58:53 -0700268 {
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700269 endpointMessageQueues[eid]->requestQueue.push_back(inputRequest);
270 }
271 else
272 {
273 std::deque<std::shared_ptr<RegisteredRequest>> reqQueue;
274 reqQueue.push_back(inputRequest);
275 endpointMessageQueues[eid] =
276 std::make_shared<EndpointMessageQueue>(eid, reqQueue, false);
Tom Joseph74f27c72021-05-16 07:58:53 -0700277 }
278
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700279 /* try to send new request if the endpoint is free */
280 pollEndpointQueue(eid);
Tom Joseph74f27c72021-05-16 07:58:53 -0700281
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700282 return PLDM_SUCCESS;
Tom Joseph74f27c72021-05-16 07:58:53 -0700283 }
284
Gilbert Chena85c69d2024-01-09 21:25:26 +0700285 /** @brief Unregister a PLDM request message
286 *
287 * @param[in] eid - endpoint ID of the remote MCTP endpoint
288 * @param[in] instanceId - instance ID to match request and response
289 * @param[in] type - PLDM type
290 * @param[in] command - PLDM command
291 *
292 * @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise
293 */
294 int unregisterRequest(mctp_eid_t eid, uint8_t instanceId, uint8_t type,
295 uint8_t command)
296 {
297 RequestKey key{eid, instanceId, type, command};
298
299 /* handlers only contain key when the message is already sent */
300 if (handlers.contains(key))
301 {
302 auto& [request, responseHandler, timerInstance] = handlers[key];
303 request->stop();
304 auto rc = timerInstance->stop();
305 if (rc)
306 {
307 error(
308 "Failed to stop the instance ID expiry timer, response code '{RC}'",
309 "RC", static_cast<int>(rc));
310 }
311
312 instanceIdDb.free(key.eid, key.instanceId);
313 handlers.erase(key);
314 endpointMessageQueues[eid]->activeRequest = false;
315 /* try to send new request if the endpoint is free */
316 pollEndpointQueue(eid);
317
318 return PLDM_SUCCESS;
319 }
320 else
321 {
322 if (!endpointMessageQueues.contains(eid))
323 {
324 error(
325 "Can't find request for EID '{EID}' is using InstanceID '{INSTANCEID}' in Endpoint message Queue",
326 "EID", (unsigned)eid, "INSTANCEID", (unsigned)instanceId);
327 return PLDM_ERROR;
328 }
329 auto requestMsg = endpointMessageQueues[eid]->requestQueue;
330 /* Find the registered request in the requestQueue */
331 for (auto it = requestMsg.begin(); it != requestMsg.end();)
332 {
333 auto msg = *it;
334 if (msg->key == key)
335 {
336 // erase and get the next valid iterator
337 it = endpointMessageQueues[eid]->requestQueue.erase(it);
338 instanceIdDb.free(key.eid, key.instanceId);
339 return PLDM_SUCCESS;
340 }
341 else
342 {
343 ++it; // increment iterator only if not erasing
344 }
345 }
346 }
347
348 return PLDM_ERROR;
349 }
350
Tom Joseph74f27c72021-05-16 07:58:53 -0700351 /** @brief Handle PLDM response message
352 *
353 * @param[in] eid - endpoint ID of the remote MCTP endpoint
354 * @param[in] instanceId - instance ID to match request and response
355 * @param[in] type - PLDM type
356 * @param[in] command - PLDM command
357 * @param[in] response - PLDM response message
358 * @param[in] respMsgLen - length of the response message
359 */
360 void handleResponse(mctp_eid_t eid, uint8_t instanceId, uint8_t type,
361 uint8_t command, const pldm_msg* response,
362 size_t respMsgLen)
363 {
364 RequestKey key{eid, instanceId, type, command};
365 if (handlers.contains(key))
366 {
367 auto& [request, responseHandler, timerInstance] = handlers[key];
368 request->stop();
369 auto rc = timerInstance->stop();
370 if (rc)
371 {
Riya Dixit087a7512024-04-06 14:28:08 -0500372 error(
373 "Failed to stop the instance ID expiry timer, response code '{RC}'",
Riya Dixit1e5c81e2024-05-03 07:54:00 -0500374 "RC", rc);
Tom Joseph74f27c72021-05-16 07:58:53 -0700375 }
376 responseHandler(eid, response, respMsgLen);
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930377 instanceIdDb.free(key.eid, key.instanceId);
Tom Joseph74f27c72021-05-16 07:58:53 -0700378 handlers.erase(key);
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700379
380 endpointMessageQueues[eid]->activeRequest = false;
381 /* try to send new request if the endpoint is free */
382 pollEndpointQueue(eid);
Tom Joseph74f27c72021-05-16 07:58:53 -0700383 }
384 else
385 {
386 // Got a response for a PLDM request message not registered with the
387 // request handler, so freeing up the instance ID, this can be other
388 // OpenBMC applications relying on PLDM D-Bus apis like
389 // openpower-occ-control and softoff
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930390 instanceIdDb.free(key.eid, key.instanceId);
Tom Joseph74f27c72021-05-16 07:58:53 -0700391 }
392 }
393
Gilbert Chena85c69d2024-01-09 21:25:26 +0700394 /** @brief Wrap registerRequest with coroutine API.
395 *
396 * @return A tuple of [return_code, pldm::Response].
397 * pldm::Response is empty on non-zero return_code.
398 * Otherwise, filled with pldm_msg* content.
399 */
400 stdexec::sender auto sendRecvMsg(mctp_eid_t eid, pldm::Request&& request);
401
Tom Joseph74f27c72021-05-16 07:58:53 -0700402 private:
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +1000403 PldmTransport* pldmTransport; //!< PLDM transport object
Brad Bishop5079ac42021-08-19 18:35:06 -0400404 sdeventplus::Event& event; //!< reference to PLDM daemon's main event loop
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930405 pldm::InstanceIdDb& instanceIdDb; //!< reference to an InstanceIdDb
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930406 bool verbose; //!< verbose tracing flag
Brad Bishop5079ac42021-08-19 18:35:06 -0400407 std::chrono::seconds
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930408 instanceIdExpiryInterval; //!< Instance ID expiration interval
409 uint8_t numRetries; //!< number of request retries
Brad Bishop5079ac42021-08-19 18:35:06 -0400410 std::chrono::milliseconds
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930411 responseTimeOut; //!< time to wait between each retry
Tom Joseph74f27c72021-05-16 07:58:53 -0700412
Tom Josephe5268cd2021-09-07 13:04:03 +0530413 /** @brief Container for storing the details of the PLDM request
414 * message, handler for the corresponding PLDM response and the
415 * timer object for the Instance ID expiration
Tom Joseph74f27c72021-05-16 07:58:53 -0700416 */
Brad Bishop5079ac42021-08-19 18:35:06 -0400417 using RequestValue =
418 std::tuple<std::unique_ptr<RequestInterface>, ResponseHandler,
Patrick Williams35535cf2023-12-05 12:45:02 -0600419 std::unique_ptr<sdbusplus::Timer>>;
Tom Joseph74f27c72021-05-16 07:58:53 -0700420
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700421 // Manage the requests of responders base on MCTP EID
422 std::map<mctp_eid_t, std::shared_ptr<EndpointMessageQueue>>
423 endpointMessageQueues;
424
Tom Joseph74f27c72021-05-16 07:58:53 -0700425 /** @brief Container for storing the PLDM request entries */
426 std::unordered_map<RequestKey, RequestValue, RequestKeyHasher> handlers;
427
428 /** @brief Container to store information about the request entries to be
429 * removed after the instance ID timer expires
430 */
Brad Bishop5079ac42021-08-19 18:35:06 -0400431 std::unordered_map<RequestKey, std::unique_ptr<sdeventplus::source::Defer>,
432 RequestKeyHasher>
Tom Joseph74f27c72021-05-16 07:58:53 -0700433 removeRequestContainer;
434
435 /** @brief Remove request entry for which the instance ID expired
436 *
437 * @param[in] key - key for the Request
438 */
439 void removeRequestEntry(RequestKey key)
440 {
441 if (removeRequestContainer.contains(key))
442 {
443 removeRequestContainer[key].reset();
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930444 instanceIdDb.free(key.eid, key.instanceId);
Tom Joseph74f27c72021-05-16 07:58:53 -0700445 handlers.erase(key);
446 removeRequestContainer.erase(key);
447 }
448 }
449};
450
Gilbert Chena85c69d2024-01-09 21:25:26 +0700451/** @class SendRecvMsgOperation
452 *
453 * Represents the state and logic for a single send/receive message operation
454 *
455 * @tparam RequestInterface - Request class type
456 * @tparam stdexec::receiver - Execute receiver
457 */
458template <class RequestInterface, stdexec::receiver R>
459struct SendRecvMsgOperation
460{
461 SendRecvMsgOperation() = delete;
462
463 explicit SendRecvMsgOperation(Handler<RequestInterface>& handler,
464 mctp_eid_t eid, pldm::Request&& request,
465 R&& r) :
466 handler(handler),
467 request(std::move(request)), receiver(std::move(r))
468 {
469 auto requestMsg =
470 reinterpret_cast<const pldm_msg*>(this->request.data());
471 requestKey = RequestKey{
472 eid,
473 requestMsg->hdr.instance_id,
474 requestMsg->hdr.type,
475 requestMsg->hdr.command,
476 };
477 response = nullptr;
478 respMsgLen = 0;
479 }
480
481 /** @brief Checks if the operation has been requested to stop.
482 * If so, it sets the state to stopped.Registers the request with
483 * the handler. If registration fails, sets an error on the
484 * receiver. If stopping is possible, sets up a stop callback.
485 *
486 * @param[in] op - operation request
487 *
488 * @return Execute errors
489 */
490 friend void tag_invoke(stdexec::start_t, SendRecvMsgOperation& op) noexcept
491 {
492 auto stopToken = stdexec::get_stop_token(stdexec::get_env(op.receiver));
493
494 // operation already cancelled
495 if (stopToken.stop_requested())
496 {
497 return stdexec::set_stopped(std::move(op.receiver));
498 }
499
500 using namespace std::placeholders;
501 auto rc = op.handler.registerRequest(
502 op.requestKey.eid, op.requestKey.instanceId, op.requestKey.type,
503 op.requestKey.command, std::move(op.request),
504 std::bind(&SendRecvMsgOperation::onComplete, &op, _1, _2, _3));
505 if (rc)
506 {
507 return stdexec::set_error(std::move(op.receiver), rc);
508 }
509
510 if (stopToken.stop_possible())
511 {
512 op.stopCallback.emplace(
513 std::move(stopToken),
514 std::bind(&SendRecvMsgOperation::onStop, &op));
515 }
516 }
517
518 /** @brief Unregisters the request and sets the state to stopped on the
519 * receiver.
520 */
521 void onStop()
522 {
523 handler.unregisterRequest(requestKey.eid, requestKey.instanceId,
524 requestKey.type, requestKey.command);
525 return stdexec::set_stopped(std::move(receiver));
526 }
527
528 /** @brief This function resets the stop callback. Validates the response
529 * and sets either an error or a value on the receiver.
530 *
531 * @param[in] eid - endpoint ID of the remote MCTP endpoint
532 * @param[in] response - PLDM response message
533 * @param[in] respMsgLen - length of the response message
534 *
535 * @return PLDM completion code
536 */
537 void onComplete(mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen)
538 {
539 stopCallback.reset();
540 assert(eid == this->requestKey.eid);
541 if (!response || !respMsgLen)
542 {
543 return stdexec::set_error(std::move(receiver),
544 static_cast<int>(PLDM_ERROR));
545 }
546 else
547 {
548 return stdexec::set_value(std::move(receiver), response,
549 respMsgLen);
550 }
551 }
552
553 private:
554 /** @brief Reference to a Handler object that manages the request/response
555 * logic.
556 */
557 requester::Handler<RequestInterface>& handler;
558
559 /** @brief Stores information about the request such as eid, instanceId,
560 * type, and command.
561 */
562 RequestKey requestKey;
563
564 /** @brief The request message to be sent.
565 */
566 pldm::Request request;
567
568 /** @brief The response message for the sent request message.
569 */
570 const pldm_msg* response;
571
572 /** @brief The length of response message for the sent request message.
573 */
574 size_t respMsgLen;
575
576 /** @brief The receiver to be notified with the result of the operation.
577 */
578 R receiver;
579
580 /** @brief An optional callback that handles stopping the operation if
581 * requested.
582 */
583 std::optional<typename stdexec::stop_token_of_t<
584 stdexec::env_of_t<R>>::template callback_type<std::function<void()>>>
585 stopCallback = std::nullopt;
586};
587
588/** @class SendRecvMsgSender
589 *
590 * Represents the single message sender
591 *
592 * @tparam RequestInterface - Request class type
593 */
594template <class RequestInterface>
595struct SendRecvMsgSender
596{
597 using is_sender = void;
598
599 SendRecvMsgSender() = delete;
600
601 explicit SendRecvMsgSender(requester::Handler<RequestInterface>& handler,
602 mctp_eid_t eid, pldm::Request&& request) :
603 handler(handler),
604 eid(eid), request(std::move(request))
605 {}
606
607 friend auto tag_invoke(stdexec::get_completion_signatures_t,
608 const SendRecvMsgSender&, auto)
609 -> stdexec::completion_signatures<
610 stdexec::set_value_t(const pldm_msg*, size_t),
611 stdexec::set_error_t(int), stdexec::set_stopped_t()>;
612
613 /** @brief Execute the sending the request message */
614 template <stdexec::receiver R>
615 friend auto tag_invoke(stdexec::connect_t, SendRecvMsgSender&& self, R r)
616 {
617 return SendRecvMsgOperation<RequestInterface, R>(
618 self.handler, self.eid, std::move(self.request), std::move(r));
619 }
620
621 private:
622 /** @brief Reference to a Handler object that manages the request/response
623 * logic.
624 */
625 requester::Handler<RequestInterface>& handler;
626
627 /** @brief MCTP Endpoint ID of request message */
628 mctp_eid_t eid;
629
630 /** @brief Request message */
631 pldm::Request request;
632};
633
634/** @brief This function handles sending the request message and responses the
635 * response message for the caller.
636 *
637 * @param[in] eid - endpoint ID of the remote MCTP endpoint
638 * @param[in] request - PLDM request message
639 *
640 * @return The reponse message and response message length.
641 */
642template <class RequestInterface>
643stdexec::sender auto
644 Handler<RequestInterface>::sendRecvMsg(mctp_eid_t eid,
645 pldm::Request&& request)
646{
647 return SendRecvMsgSender(*this, eid, std::move(request)) |
648 stdexec::then([](const pldm_msg* responseMsg, size_t respMsgLen) {
649 return std::make_tuple(responseMsg, respMsgLen);
650 });
651}
652
Tom Joseph74f27c72021-05-16 07:58:53 -0700653} // namespace requester
654
655} // namespace pldm