blob: c66582211588011b6a4f7cf53da4b4c889187860 [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 Nguyen6b901e42024-07-10 15:21:10 +070070/** @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 */
79using SendRecvCoResp = std::tuple<int, const pldm_msg*, size_t>;
80
Thu Nguyen4ddee3a2023-08-03 08:20:03 +070081/** @struct RegisteredRequest
82 *
83 * This struct is used to store the registered request to one endpoint.
84 */
85struct 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 */
97struct 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
103 bool operator==(const mctp_eid_t& mctpEid) const
104 {
105 return (eid == mctpEid);
106 }
107};
108
Tom Joseph74f27c72021-05-16 07:58:53 -0700109/** @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 */
120template <class RequestInterface>
121class Handler
122{
Tom Joseph74f27c72021-05-16 07:58:53 -0700123 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 *
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +1000133 * @param[in] pldm_transport - PLDM requester
Tom Joseph74f27c72021-05-16 07:58:53 -0700134 * @param[in] event - reference to PLDM daemon's main event loop
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930135 * @param[in] instanceIdDb - reference to an InstanceIdDb
Tom Josephe5268cd2021-09-07 13:04:03 +0530136 * @param[in] verbose - verbose tracing flag
Tom Joseph74f27c72021-05-16 07:58:53 -0700137 * @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 */
141 explicit Handler(
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +1000142 PldmTransport* pldmTransport, sdeventplus::Event& event,
143 pldm::InstanceIdDb& instanceIdDb, bool verbose,
Brad Bishop5079ac42021-08-19 18:35:06 -0400144 std::chrono::seconds instanceIdExpiryInterval =
145 std::chrono::seconds(INSTANCE_ID_EXPIRATION_INTERVAL),
Tom Joseph74f27c72021-05-16 07:58:53 -0700146 uint8_t numRetries = static_cast<uint8_t>(NUMBER_OF_REQUEST_RETRIES),
Brad Bishop5079ac42021-08-19 18:35:06 -0400147 std::chrono::milliseconds responseTimeOut =
148 std::chrono::milliseconds(RESPONSE_TIME_OUT)) :
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +1000149 pldmTransport(pldmTransport),
150 event(event), instanceIdDb(instanceIdDb), verbose(verbose),
Tom Joseph74f27c72021-05-16 07:58:53 -0700151 instanceIdExpiryInterval(instanceIdExpiryInterval),
152 numRetries(numRetries), responseTimeOut(responseTimeOut)
153 {}
154
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700155 void instanceIdExpiryCallBack(RequestKey key)
156 {
157 auto eid = key.eid;
158 if (this->handlers.contains(key))
159 {
Riya Dixit087a7512024-04-06 14:28:08 -0500160 info(
161 "Instance ID expiry for EID '{EID}' using InstanceID '{INSTANCEID}'",
Riya Dixit1e5c81e2024-05-03 07:54:00 -0500162 "EID", key.eid, "INSTANCEID", key.instanceId);
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700163 auto& [request, responseHandler,
164 timerInstance] = this->handlers[key];
165 request->stop();
166 auto rc = timerInstance->stop();
167 if (rc)
168 {
Riya Dixit087a7512024-04-06 14:28:08 -0500169 error(
170 "Failed to stop the instance ID expiry timer, response code '{RC}'",
Riya Dixit1e5c81e2024-05-03 07:54:00 -0500171 "RC", rc);
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700172 }
173 // Call response handler with an empty response to indicate no
174 // response
175 responseHandler(eid, nullptr, 0);
176 this->removeRequestContainer.emplace(
177 key,
178 std::make_unique<sdeventplus::source::Defer>(
179 event, std::bind(&Handler::removeRequestEntry, this, key)));
180 endpointMessageQueues[eid]->activeRequest = false;
181
182 /* try to send new request if the endpoint is free */
183 pollEndpointQueue(eid);
184 }
185 else
186 {
187 // This condition is not possible, if a response is received
188 // before the instance ID expiry, then the response handler
189 // is executed and the entry will be removed.
190 assert(false);
191 }
192 }
193
194 /** @brief Send the remaining PLDM request messages in endpoint queue
195 *
196 * @param[in] eid - endpoint ID of the remote MCTP endpoint
197 */
198 int pollEndpointQueue(mctp_eid_t eid)
199 {
200 if (endpointMessageQueues[eid]->activeRequest ||
201 endpointMessageQueues[eid]->requestQueue.empty())
202 {
203 return PLDM_SUCCESS;
204 }
205
206 endpointMessageQueues[eid]->activeRequest = true;
207 auto requestMsg = endpointMessageQueues[eid]->requestQueue.front();
208 endpointMessageQueues[eid]->requestQueue.pop_front();
209
210 auto request = std::make_unique<RequestInterface>(
211 pldmTransport, requestMsg->key.eid, event,
212 std::move(requestMsg->reqMsg), numRetries, responseTimeOut,
213 verbose);
Patrick Williams35535cf2023-12-05 12:45:02 -0600214 auto timer = std::make_unique<sdbusplus::Timer>(
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700215 event.get(), std::bind(&Handler::instanceIdExpiryCallBack, this,
216 requestMsg->key));
217
218 auto rc = request->start();
219 if (rc)
220 {
221 instanceIdDb.free(requestMsg->key.eid, requestMsg->key.instanceId);
Riya Dixit087a7512024-04-06 14:28:08 -0500222 error(
223 "Failure to send the PLDM request message for polling endpoint queue, response code '{RC}'",
224 "RC", rc);
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700225 endpointMessageQueues[eid]->activeRequest = false;
226 return rc;
227 }
228
229 try
230 {
231 timer->start(duration_cast<std::chrono::microseconds>(
232 instanceIdExpiryInterval));
233 }
234 catch (const std::runtime_error& e)
235 {
236 instanceIdDb.free(requestMsg->key.eid, requestMsg->key.instanceId);
237 error(
Riya Dixit087a7512024-04-06 14:28:08 -0500238 "Failed to start the instance ID expiry timer, error - {ERROR}",
239 "ERROR", e);
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700240 endpointMessageQueues[eid]->activeRequest = false;
241 return PLDM_ERROR;
242 }
243
244 handlers.emplace(requestMsg->key,
245 std::make_tuple(std::move(request),
246 std::move(requestMsg->responseHandler),
247 std::move(timer)));
248 return PLDM_SUCCESS;
249 }
250
Tom Joseph74f27c72021-05-16 07:58:53 -0700251 /** @brief Register a PLDM request message
252 *
253 * @param[in] eid - endpoint ID of the remote MCTP endpoint
254 * @param[in] instanceId - instance ID to match request and response
255 * @param[in] type - PLDM type
256 * @param[in] command - PLDM command
257 * @param[in] requestMsg - PLDM request message
258 * @param[in] responseHandler - Response handler for this request
259 *
260 * @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise
261 */
262 int registerRequest(mctp_eid_t eid, uint8_t instanceId, uint8_t type,
263 uint8_t command, pldm::Request&& requestMsg,
264 ResponseHandler&& responseHandler)
265 {
266 RequestKey key{eid, instanceId, type, command};
267
Thu Nguyenb4e8cfd2023-03-23 15:06:44 +0700268 if (handlers.contains(key))
269 {
Riya Dixit087a7512024-04-06 14:28:08 -0500270 error(
271 "Register request for EID '{EID}' is using InstanceID '{INSTANCEID}'",
Riya Dixit1e5c81e2024-05-03 07:54:00 -0500272 "EID", eid, "INSTANCEID", instanceId);
Thu Nguyenb4e8cfd2023-03-23 15:06:44 +0700273 return PLDM_ERROR;
274 }
275
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700276 auto inputRequest = std::make_shared<RegisteredRequest>(
277 key, std::move(requestMsg), std::move(responseHandler));
278 if (endpointMessageQueues.contains(eid))
Tom Joseph74f27c72021-05-16 07:58:53 -0700279 {
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700280 endpointMessageQueues[eid]->requestQueue.push_back(inputRequest);
281 }
282 else
283 {
284 std::deque<std::shared_ptr<RegisteredRequest>> reqQueue;
285 reqQueue.push_back(inputRequest);
286 endpointMessageQueues[eid] =
287 std::make_shared<EndpointMessageQueue>(eid, reqQueue, false);
Tom Joseph74f27c72021-05-16 07:58:53 -0700288 }
289
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700290 /* try to send new request if the endpoint is free */
291 pollEndpointQueue(eid);
Tom Joseph74f27c72021-05-16 07:58:53 -0700292
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700293 return PLDM_SUCCESS;
Tom Joseph74f27c72021-05-16 07:58:53 -0700294 }
295
Gilbert Chena85c69d2024-01-09 21:25:26 +0700296 /** @brief Unregister a PLDM request message
297 *
298 * @param[in] eid - endpoint ID of the remote MCTP endpoint
299 * @param[in] instanceId - instance ID to match request and response
300 * @param[in] type - PLDM type
301 * @param[in] command - PLDM command
302 *
303 * @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise
304 */
305 int unregisterRequest(mctp_eid_t eid, uint8_t instanceId, uint8_t type,
306 uint8_t command)
307 {
308 RequestKey key{eid, instanceId, type, command};
309
310 /* handlers only contain key when the message is already sent */
311 if (handlers.contains(key))
312 {
313 auto& [request, responseHandler, timerInstance] = handlers[key];
314 request->stop();
315 auto rc = timerInstance->stop();
316 if (rc)
317 {
318 error(
319 "Failed to stop the instance ID expiry timer, response code '{RC}'",
320 "RC", static_cast<int>(rc));
321 }
322
323 instanceIdDb.free(key.eid, key.instanceId);
324 handlers.erase(key);
325 endpointMessageQueues[eid]->activeRequest = false;
326 /* try to send new request if the endpoint is free */
327 pollEndpointQueue(eid);
328
329 return PLDM_SUCCESS;
330 }
331 else
332 {
333 if (!endpointMessageQueues.contains(eid))
334 {
335 error(
336 "Can't find request for EID '{EID}' is using InstanceID '{INSTANCEID}' in Endpoint message Queue",
337 "EID", (unsigned)eid, "INSTANCEID", (unsigned)instanceId);
338 return PLDM_ERROR;
339 }
340 auto requestMsg = endpointMessageQueues[eid]->requestQueue;
341 /* Find the registered request in the requestQueue */
342 for (auto it = requestMsg.begin(); it != requestMsg.end();)
343 {
344 auto msg = *it;
345 if (msg->key == key)
346 {
347 // erase and get the next valid iterator
348 it = endpointMessageQueues[eid]->requestQueue.erase(it);
349 instanceIdDb.free(key.eid, key.instanceId);
350 return PLDM_SUCCESS;
351 }
352 else
353 {
354 ++it; // increment iterator only if not erasing
355 }
356 }
357 }
358
359 return PLDM_ERROR;
360 }
361
Tom Joseph74f27c72021-05-16 07:58:53 -0700362 /** @brief Handle PLDM response message
363 *
364 * @param[in] eid - endpoint ID of the remote MCTP endpoint
365 * @param[in] instanceId - instance ID to match request and response
366 * @param[in] type - PLDM type
367 * @param[in] command - PLDM command
368 * @param[in] response - PLDM response message
369 * @param[in] respMsgLen - length of the response message
370 */
371 void handleResponse(mctp_eid_t eid, uint8_t instanceId, uint8_t type,
372 uint8_t command, const pldm_msg* response,
373 size_t respMsgLen)
374 {
375 RequestKey key{eid, instanceId, type, command};
376 if (handlers.contains(key))
377 {
378 auto& [request, responseHandler, timerInstance] = handlers[key];
379 request->stop();
380 auto rc = timerInstance->stop();
381 if (rc)
382 {
Riya Dixit087a7512024-04-06 14:28:08 -0500383 error(
384 "Failed to stop the instance ID expiry timer, response code '{RC}'",
Riya Dixit1e5c81e2024-05-03 07:54:00 -0500385 "RC", rc);
Tom Joseph74f27c72021-05-16 07:58:53 -0700386 }
387 responseHandler(eid, response, respMsgLen);
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930388 instanceIdDb.free(key.eid, key.instanceId);
Tom Joseph74f27c72021-05-16 07:58:53 -0700389 handlers.erase(key);
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700390
391 endpointMessageQueues[eid]->activeRequest = false;
392 /* try to send new request if the endpoint is free */
393 pollEndpointQueue(eid);
Tom Joseph74f27c72021-05-16 07:58:53 -0700394 }
395 else
396 {
397 // Got a response for a PLDM request message not registered with the
398 // request handler, so freeing up the instance ID, this can be other
399 // OpenBMC applications relying on PLDM D-Bus apis like
400 // openpower-occ-control and softoff
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930401 instanceIdDb.free(key.eid, key.instanceId);
Tom Joseph74f27c72021-05-16 07:58:53 -0700402 }
403 }
404
Gilbert Chena85c69d2024-01-09 21:25:26 +0700405 /** @brief Wrap registerRequest with coroutine API.
406 *
Thu Nguyen6b901e42024-07-10 15:21:10 +0700407 * @return Return [PLDM_ERROR, _, _] if registerRequest fails.
408 * Return [PLDM_ERROR_NOT_READY, nullptr, 0] if timed out.
409 * Return [PLDM_SUCCESS, resp, len] if succeeded
Gilbert Chena85c69d2024-01-09 21:25:26 +0700410 */
Thu Nguyen6b901e42024-07-10 15:21:10 +0700411 stdexec::sender_of<stdexec::set_value_t(SendRecvCoResp)> auto
412 sendRecvMsg(mctp_eid_t eid, pldm::Request&& request);
Gilbert Chena85c69d2024-01-09 21:25:26 +0700413
Tom Joseph74f27c72021-05-16 07:58:53 -0700414 private:
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +1000415 PldmTransport* pldmTransport; //!< PLDM transport object
Brad Bishop5079ac42021-08-19 18:35:06 -0400416 sdeventplus::Event& event; //!< reference to PLDM daemon's main event loop
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930417 pldm::InstanceIdDb& instanceIdDb; //!< reference to an InstanceIdDb
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930418 bool verbose; //!< verbose tracing flag
Brad Bishop5079ac42021-08-19 18:35:06 -0400419 std::chrono::seconds
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930420 instanceIdExpiryInterval; //!< Instance ID expiration interval
421 uint8_t numRetries; //!< number of request retries
Brad Bishop5079ac42021-08-19 18:35:06 -0400422 std::chrono::milliseconds
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930423 responseTimeOut; //!< time to wait between each retry
Tom Joseph74f27c72021-05-16 07:58:53 -0700424
Tom Josephe5268cd2021-09-07 13:04:03 +0530425 /** @brief Container for storing the details of the PLDM request
426 * message, handler for the corresponding PLDM response and the
427 * timer object for the Instance ID expiration
Tom Joseph74f27c72021-05-16 07:58:53 -0700428 */
Brad Bishop5079ac42021-08-19 18:35:06 -0400429 using RequestValue =
430 std::tuple<std::unique_ptr<RequestInterface>, ResponseHandler,
Patrick Williams35535cf2023-12-05 12:45:02 -0600431 std::unique_ptr<sdbusplus::Timer>>;
Tom Joseph74f27c72021-05-16 07:58:53 -0700432
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700433 // Manage the requests of responders base on MCTP EID
434 std::map<mctp_eid_t, std::shared_ptr<EndpointMessageQueue>>
435 endpointMessageQueues;
436
Tom Joseph74f27c72021-05-16 07:58:53 -0700437 /** @brief Container for storing the PLDM request entries */
438 std::unordered_map<RequestKey, RequestValue, RequestKeyHasher> handlers;
439
440 /** @brief Container to store information about the request entries to be
441 * removed after the instance ID timer expires
442 */
Brad Bishop5079ac42021-08-19 18:35:06 -0400443 std::unordered_map<RequestKey, std::unique_ptr<sdeventplus::source::Defer>,
444 RequestKeyHasher>
Tom Joseph74f27c72021-05-16 07:58:53 -0700445 removeRequestContainer;
446
447 /** @brief Remove request entry for which the instance ID expired
448 *
449 * @param[in] key - key for the Request
450 */
451 void removeRequestEntry(RequestKey key)
452 {
453 if (removeRequestContainer.contains(key))
454 {
455 removeRequestContainer[key].reset();
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930456 instanceIdDb.free(key.eid, key.instanceId);
Tom Joseph74f27c72021-05-16 07:58:53 -0700457 handlers.erase(key);
458 removeRequestContainer.erase(key);
459 }
460 }
461};
462
Gilbert Chena85c69d2024-01-09 21:25:26 +0700463/** @class SendRecvMsgOperation
464 *
465 * Represents the state and logic for a single send/receive message operation
466 *
467 * @tparam RequestInterface - Request class type
468 * @tparam stdexec::receiver - Execute receiver
469 */
470template <class RequestInterface, stdexec::receiver R>
471struct SendRecvMsgOperation
472{
473 SendRecvMsgOperation() = delete;
474
475 explicit SendRecvMsgOperation(Handler<RequestInterface>& handler,
476 mctp_eid_t eid, pldm::Request&& request,
477 R&& r) :
478 handler(handler),
479 request(std::move(request)), receiver(std::move(r))
480 {
481 auto requestMsg =
482 reinterpret_cast<const pldm_msg*>(this->request.data());
483 requestKey = RequestKey{
484 eid,
485 requestMsg->hdr.instance_id,
486 requestMsg->hdr.type,
487 requestMsg->hdr.command,
488 };
489 response = nullptr;
490 respMsgLen = 0;
491 }
492
493 /** @brief Checks if the operation has been requested to stop.
494 * If so, it sets the state to stopped.Registers the request with
495 * the handler. If registration fails, sets an error on the
496 * receiver. If stopping is possible, sets up a stop callback.
497 *
498 * @param[in] op - operation request
499 *
500 * @return Execute errors
501 */
502 friend void tag_invoke(stdexec::start_t, SendRecvMsgOperation& op) noexcept
503 {
504 auto stopToken = stdexec::get_stop_token(stdexec::get_env(op.receiver));
505
506 // operation already cancelled
507 if (stopToken.stop_requested())
508 {
509 return stdexec::set_stopped(std::move(op.receiver));
510 }
511
512 using namespace std::placeholders;
513 auto rc = op.handler.registerRequest(
514 op.requestKey.eid, op.requestKey.instanceId, op.requestKey.type,
515 op.requestKey.command, std::move(op.request),
516 std::bind(&SendRecvMsgOperation::onComplete, &op, _1, _2, _3));
517 if (rc)
518 {
Thu Nguyen6b901e42024-07-10 15:21:10 +0700519 return stdexec::set_value(std::move(op.receiver), rc,
520 static_cast<const pldm_msg*>(nullptr),
521 static_cast<size_t>(0));
Gilbert Chena85c69d2024-01-09 21:25:26 +0700522 }
523
524 if (stopToken.stop_possible())
525 {
526 op.stopCallback.emplace(
527 std::move(stopToken),
528 std::bind(&SendRecvMsgOperation::onStop, &op));
529 }
530 }
531
532 /** @brief Unregisters the request and sets the state to stopped on the
533 * receiver.
534 */
535 void onStop()
536 {
537 handler.unregisterRequest(requestKey.eid, requestKey.instanceId,
538 requestKey.type, requestKey.command);
539 return stdexec::set_stopped(std::move(receiver));
540 }
541
542 /** @brief This function resets the stop callback. Validates the response
543 * and sets either an error or a value on the receiver.
544 *
545 * @param[in] eid - endpoint ID of the remote MCTP endpoint
546 * @param[in] response - PLDM response message
547 * @param[in] respMsgLen - length of the response message
548 *
549 * @return PLDM completion code
550 */
551 void onComplete(mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen)
552 {
553 stopCallback.reset();
554 assert(eid == this->requestKey.eid);
Thu Nguyen6b901e42024-07-10 15:21:10 +0700555 auto rc = PLDM_SUCCESS;
556 if (!response && !respMsgLen)
Gilbert Chena85c69d2024-01-09 21:25:26 +0700557 {
Thu Nguyen6b901e42024-07-10 15:21:10 +0700558 rc = PLDM_ERROR_NOT_READY;
Gilbert Chena85c69d2024-01-09 21:25:26 +0700559 }
Thu Nguyen6b901e42024-07-10 15:21:10 +0700560 return stdexec::set_value(std::move(receiver), static_cast<int>(rc),
561 response, respMsgLen);
Gilbert Chena85c69d2024-01-09 21:25:26 +0700562 }
563
564 private:
565 /** @brief Reference to a Handler object that manages the request/response
566 * logic.
567 */
568 requester::Handler<RequestInterface>& handler;
569
570 /** @brief Stores information about the request such as eid, instanceId,
571 * type, and command.
572 */
573 RequestKey requestKey;
574
575 /** @brief The request message to be sent.
576 */
577 pldm::Request request;
578
579 /** @brief The response message for the sent request message.
580 */
581 const pldm_msg* response;
582
583 /** @brief The length of response message for the sent request message.
584 */
585 size_t respMsgLen;
586
587 /** @brief The receiver to be notified with the result of the operation.
588 */
589 R receiver;
590
591 /** @brief An optional callback that handles stopping the operation if
592 * requested.
593 */
594 std::optional<typename stdexec::stop_token_of_t<
595 stdexec::env_of_t<R>>::template callback_type<std::function<void()>>>
596 stopCallback = std::nullopt;
597};
598
599/** @class SendRecvMsgSender
600 *
601 * Represents the single message sender
602 *
603 * @tparam RequestInterface - Request class type
604 */
605template <class RequestInterface>
606struct SendRecvMsgSender
607{
608 using is_sender = void;
609
610 SendRecvMsgSender() = delete;
611
612 explicit SendRecvMsgSender(requester::Handler<RequestInterface>& handler,
613 mctp_eid_t eid, pldm::Request&& request) :
614 handler(handler),
615 eid(eid), request(std::move(request))
616 {}
617
618 friend auto tag_invoke(stdexec::get_completion_signatures_t,
619 const SendRecvMsgSender&, auto)
620 -> stdexec::completion_signatures<
Thu Nguyen6b901e42024-07-10 15:21:10 +0700621 stdexec::set_value_t(int, const pldm_msg*, size_t),
622 stdexec::set_stopped_t()>;
Gilbert Chena85c69d2024-01-09 21:25:26 +0700623
624 /** @brief Execute the sending the request message */
625 template <stdexec::receiver R>
626 friend auto tag_invoke(stdexec::connect_t, SendRecvMsgSender&& self, R r)
627 {
628 return SendRecvMsgOperation<RequestInterface, R>(
629 self.handler, self.eid, std::move(self.request), std::move(r));
630 }
631
632 private:
633 /** @brief Reference to a Handler object that manages the request/response
634 * logic.
635 */
636 requester::Handler<RequestInterface>& handler;
637
638 /** @brief MCTP Endpoint ID of request message */
639 mctp_eid_t eid;
640
641 /** @brief Request message */
642 pldm::Request request;
643};
644
Thu Nguyen6b901e42024-07-10 15:21:10 +0700645/** @brief Wrap registerRequest with coroutine API.
Gilbert Chena85c69d2024-01-09 21:25:26 +0700646 *
647 * @param[in] eid - endpoint ID of the remote MCTP endpoint
648 * @param[in] request - PLDM request message
649 *
Thu Nguyen6b901e42024-07-10 15:21:10 +0700650 * @return Return [PLDM_ERROR, _, _] if registerRequest fails.
651 * Return [PLDM_ERROR_NOT_READY, nullptr, 0] if timed out.
652 * Return [PLDM_SUCCESS, resp, len] if succeeded
Gilbert Chena85c69d2024-01-09 21:25:26 +0700653 */
654template <class RequestInterface>
Thu Nguyen6b901e42024-07-10 15:21:10 +0700655stdexec::sender_of<stdexec::set_value_t(SendRecvCoResp)> auto
Gilbert Chena85c69d2024-01-09 21:25:26 +0700656 Handler<RequestInterface>::sendRecvMsg(mctp_eid_t eid,
657 pldm::Request&& request)
658{
659 return SendRecvMsgSender(*this, eid, std::move(request)) |
Thu Nguyen6b901e42024-07-10 15:21:10 +0700660 stdexec::then([](int rc, const pldm_msg* resp, size_t respLen) {
661 return std::make_tuple(rc, resp, respLen);
Gilbert Chena85c69d2024-01-09 21:25:26 +0700662 });
663}
664
Tom Joseph74f27c72021-05-16 07:58:53 -0700665} // namespace requester
666
667} // namespace pldm