blob: 723623b14843dab47b50fe4071de531d42073c04 [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)) :
Patrick Williams16c2a0a2024-08-16 15:20:59 -0400149 pldmTransport(pldmTransport), event(event), instanceIdDb(instanceIdDb),
150 verbose(verbose), instanceIdExpiryInterval(instanceIdExpiryInterval),
Tom Joseph74f27c72021-05-16 07:58:53 -0700151 numRetries(numRetries), responseTimeOut(responseTimeOut)
152 {}
153
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700154 void instanceIdExpiryCallBack(RequestKey key)
155 {
156 auto eid = key.eid;
157 if (this->handlers.contains(key))
158 {
Riya Dixit087a7512024-04-06 14:28:08 -0500159 info(
160 "Instance ID expiry for EID '{EID}' using InstanceID '{INSTANCEID}'",
Riya Dixit1e5c81e2024-05-03 07:54:00 -0500161 "EID", key.eid, "INSTANCEID", key.instanceId);
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700162 auto& [request, responseHandler,
163 timerInstance] = this->handlers[key];
164 request->stop();
165 auto rc = timerInstance->stop();
166 if (rc)
167 {
Riya Dixit087a7512024-04-06 14:28:08 -0500168 error(
169 "Failed to stop the instance ID expiry timer, response code '{RC}'",
Riya Dixit1e5c81e2024-05-03 07:54:00 -0500170 "RC", rc);
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700171 }
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 */
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);
Patrick Williams35535cf2023-12-05 12:45:02 -0600213 auto timer = std::make_unique<sdbusplus::Timer>(
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700214 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);
Riya Dixit087a7512024-04-06 14:28:08 -0500221 error(
222 "Failure to send the PLDM request message for polling endpoint queue, response code '{RC}'",
223 "RC", rc);
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700224 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(
Riya Dixit087a7512024-04-06 14:28:08 -0500237 "Failed to start the instance ID expiry timer, error - {ERROR}",
238 "ERROR", e);
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700239 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
Tom Joseph74f27c72021-05-16 07:58:53 -0700250 /** @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 */
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
Thu Nguyenb4e8cfd2023-03-23 15:06:44 +0700267 if (handlers.contains(key))
268 {
Riya Dixit087a7512024-04-06 14:28:08 -0500269 error(
270 "Register request for EID '{EID}' is using InstanceID '{INSTANCEID}'",
Riya Dixit1e5c81e2024-05-03 07:54:00 -0500271 "EID", eid, "INSTANCEID", instanceId);
Thu Nguyenb4e8cfd2023-03-23 15:06:44 +0700272 return PLDM_ERROR;
273 }
274
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700275 auto inputRequest = std::make_shared<RegisteredRequest>(
276 key, std::move(requestMsg), std::move(responseHandler));
277 if (endpointMessageQueues.contains(eid))
Tom Joseph74f27c72021-05-16 07:58:53 -0700278 {
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700279 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);
Tom Joseph74f27c72021-05-16 07:58:53 -0700287 }
288
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700289 /* try to send new request if the endpoint is free */
Eric Yang4bf3ed82025-03-14 18:45:53 +0800290 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 }
Tom Joseph74f27c72021-05-16 07:58:53 -0700298
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700299 return PLDM_SUCCESS;
Tom Joseph74f27c72021-05-16 07:58:53 -0700300 }
301
Gilbert Chena85c69d2024-01-09 21:25:26 +0700302 /** @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 */
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
Tom Joseph74f27c72021-05-16 07:58:53 -0700368 /** @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 */
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};
SeanCChuangf7d249e2024-09-02 14:10:33 +0800382 if (handlers.contains(key) && !removeRequestContainer.contains(key))
Tom Joseph74f27c72021-05-16 07:58:53 -0700383 {
384 auto& [request, responseHandler, timerInstance] = handlers[key];
385 request->stop();
386 auto rc = timerInstance->stop();
387 if (rc)
388 {
Riya Dixit087a7512024-04-06 14:28:08 -0500389 error(
390 "Failed to stop the instance ID expiry timer, response code '{RC}'",
Riya Dixit1e5c81e2024-05-03 07:54:00 -0500391 "RC", rc);
Tom Joseph74f27c72021-05-16 07:58:53 -0700392 }
393 responseHandler(eid, response, respMsgLen);
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930394 instanceIdDb.free(key.eid, key.instanceId);
Tom Joseph74f27c72021-05-16 07:58:53 -0700395 handlers.erase(key);
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700396
397 endpointMessageQueues[eid]->activeRequest = false;
398 /* try to send new request if the endpoint is free */
399 pollEndpointQueue(eid);
Tom Joseph74f27c72021-05-16 07:58:53 -0700400 }
Tom Joseph74f27c72021-05-16 07:58:53 -0700401 }
402
Gilbert Chena85c69d2024-01-09 21:25:26 +0700403 /** @brief Wrap registerRequest with coroutine API.
404 *
Thu Nguyen6b901e42024-07-10 15:21:10 +0700405 * @return Return [PLDM_ERROR, _, _] if registerRequest fails.
406 * Return [PLDM_ERROR_NOT_READY, nullptr, 0] if timed out.
407 * Return [PLDM_SUCCESS, resp, len] if succeeded
Gilbert Chena85c69d2024-01-09 21:25:26 +0700408 */
Patrick Williams366507c2025-02-03 14:28:01 -0500409 stdexec::sender_of<stdexec::set_value_t(SendRecvCoResp)> auto sendRecvMsg(
410 mctp_eid_t eid, pldm::Request&& request);
Gilbert Chena85c69d2024-01-09 21:25:26 +0700411
Tom Joseph74f27c72021-05-16 07:58:53 -0700412 private:
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +1000413 PldmTransport* pldmTransport; //!< PLDM transport object
Brad Bishop5079ac42021-08-19 18:35:06 -0400414 sdeventplus::Event& event; //!< reference to PLDM daemon's main event loop
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930415 pldm::InstanceIdDb& instanceIdDb; //!< reference to an InstanceIdDb
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930416 bool verbose; //!< verbose tracing flag
Brad Bishop5079ac42021-08-19 18:35:06 -0400417 std::chrono::seconds
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930418 instanceIdExpiryInterval; //!< Instance ID expiration interval
419 uint8_t numRetries; //!< number of request retries
Brad Bishop5079ac42021-08-19 18:35:06 -0400420 std::chrono::milliseconds
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930421 responseTimeOut; //!< time to wait between each retry
Tom Joseph74f27c72021-05-16 07:58:53 -0700422
Tom Josephe5268cd2021-09-07 13:04:03 +0530423 /** @brief Container for storing the details of the PLDM request
424 * message, handler for the corresponding PLDM response and the
425 * timer object for the Instance ID expiration
Tom Joseph74f27c72021-05-16 07:58:53 -0700426 */
Brad Bishop5079ac42021-08-19 18:35:06 -0400427 using RequestValue =
428 std::tuple<std::unique_ptr<RequestInterface>, ResponseHandler,
Patrick Williams35535cf2023-12-05 12:45:02 -0600429 std::unique_ptr<sdbusplus::Timer>>;
Tom Joseph74f27c72021-05-16 07:58:53 -0700430
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700431 // Manage the requests of responders base on MCTP EID
432 std::map<mctp_eid_t, std::shared_ptr<EndpointMessageQueue>>
433 endpointMessageQueues;
434
Tom Joseph74f27c72021-05-16 07:58:53 -0700435 /** @brief Container for storing the PLDM request entries */
436 std::unordered_map<RequestKey, RequestValue, RequestKeyHasher> handlers;
437
438 /** @brief Container to store information about the request entries to be
439 * removed after the instance ID timer expires
440 */
Brad Bishop5079ac42021-08-19 18:35:06 -0400441 std::unordered_map<RequestKey, std::unique_ptr<sdeventplus::source::Defer>,
442 RequestKeyHasher>
Tom Joseph74f27c72021-05-16 07:58:53 -0700443 removeRequestContainer;
444
445 /** @brief Remove request entry for which the instance ID expired
446 *
447 * @param[in] key - key for the Request
448 */
449 void removeRequestEntry(RequestKey key)
450 {
451 if (removeRequestContainer.contains(key))
452 {
453 removeRequestContainer[key].reset();
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930454 instanceIdDb.free(key.eid, key.instanceId);
Tom Joseph74f27c72021-05-16 07:58:53 -0700455 handlers.erase(key);
456 removeRequestContainer.erase(key);
457 }
458 }
459};
460
Gilbert Chena85c69d2024-01-09 21:25:26 +0700461/** @class SendRecvMsgOperation
462 *
463 * Represents the state and logic for a single send/receive message operation
464 *
465 * @tparam RequestInterface - Request class type
466 * @tparam stdexec::receiver - Execute receiver
467 */
468template <class RequestInterface, stdexec::receiver R>
469struct SendRecvMsgOperation
470{
471 SendRecvMsgOperation() = delete;
472
473 explicit SendRecvMsgOperation(Handler<RequestInterface>& handler,
474 mctp_eid_t eid, pldm::Request&& request,
475 R&& r) :
Patrick Williams16c2a0a2024-08-16 15:20:59 -0400476 handler(handler), request(std::move(request)), receiver(std::move(r))
Gilbert Chena85c69d2024-01-09 21:25:26 +0700477 {
478 auto requestMsg =
479 reinterpret_cast<const pldm_msg*>(this->request.data());
480 requestKey = RequestKey{
481 eid,
482 requestMsg->hdr.instance_id,
483 requestMsg->hdr.type,
484 requestMsg->hdr.command,
485 };
486 response = nullptr;
487 respMsgLen = 0;
488 }
489
490 /** @brief Checks if the operation has been requested to stop.
491 * If so, it sets the state to stopped.Registers the request with
492 * the handler. If registration fails, sets an error on the
493 * receiver. If stopping is possible, sets up a stop callback.
494 *
495 * @param[in] op - operation request
496 *
497 * @return Execute errors
498 */
499 friend void tag_invoke(stdexec::start_t, SendRecvMsgOperation& op) noexcept
500 {
501 auto stopToken = stdexec::get_stop_token(stdexec::get_env(op.receiver));
502
503 // operation already cancelled
504 if (stopToken.stop_requested())
505 {
506 return stdexec::set_stopped(std::move(op.receiver));
507 }
508
509 using namespace std::placeholders;
510 auto rc = op.handler.registerRequest(
511 op.requestKey.eid, op.requestKey.instanceId, op.requestKey.type,
512 op.requestKey.command, std::move(op.request),
513 std::bind(&SendRecvMsgOperation::onComplete, &op, _1, _2, _3));
514 if (rc)
515 {
Thu Nguyen6b901e42024-07-10 15:21:10 +0700516 return stdexec::set_value(std::move(op.receiver), rc,
517 static_cast<const pldm_msg*>(nullptr),
518 static_cast<size_t>(0));
Gilbert Chena85c69d2024-01-09 21:25:26 +0700519 }
520
521 if (stopToken.stop_possible())
522 {
523 op.stopCallback.emplace(
524 std::move(stopToken),
525 std::bind(&SendRecvMsgOperation::onStop, &op));
526 }
527 }
528
529 /** @brief Unregisters the request and sets the state to stopped on the
530 * receiver.
531 */
532 void onStop()
533 {
534 handler.unregisterRequest(requestKey.eid, requestKey.instanceId,
535 requestKey.type, requestKey.command);
536 return stdexec::set_stopped(std::move(receiver));
537 }
538
539 /** @brief This function resets the stop callback. Validates the response
540 * and sets either an error or a value on the receiver.
541 *
542 * @param[in] eid - endpoint ID of the remote MCTP endpoint
543 * @param[in] response - PLDM response message
544 * @param[in] respMsgLen - length of the response message
545 *
546 * @return PLDM completion code
547 */
548 void onComplete(mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen)
549 {
550 stopCallback.reset();
551 assert(eid == this->requestKey.eid);
Thu Nguyen6b901e42024-07-10 15:21:10 +0700552 auto rc = PLDM_SUCCESS;
553 if (!response && !respMsgLen)
Gilbert Chena85c69d2024-01-09 21:25:26 +0700554 {
Thu Nguyen6b901e42024-07-10 15:21:10 +0700555 rc = PLDM_ERROR_NOT_READY;
Gilbert Chena85c69d2024-01-09 21:25:26 +0700556 }
Thu Nguyen6b901e42024-07-10 15:21:10 +0700557 return stdexec::set_value(std::move(receiver), static_cast<int>(rc),
558 response, respMsgLen);
Gilbert Chena85c69d2024-01-09 21:25:26 +0700559 }
560
561 private:
562 /** @brief Reference to a Handler object that manages the request/response
563 * logic.
564 */
565 requester::Handler<RequestInterface>& handler;
566
567 /** @brief Stores information about the request such as eid, instanceId,
568 * type, and command.
569 */
570 RequestKey requestKey;
571
572 /** @brief The request message to be sent.
573 */
574 pldm::Request request;
575
576 /** @brief The response message for the sent request message.
577 */
578 const pldm_msg* response;
579
580 /** @brief The length of response message for the sent request message.
581 */
582 size_t respMsgLen;
583
584 /** @brief The receiver to be notified with the result of the operation.
585 */
586 R receiver;
587
588 /** @brief An optional callback that handles stopping the operation if
589 * requested.
590 */
591 std::optional<typename stdexec::stop_token_of_t<
592 stdexec::env_of_t<R>>::template callback_type<std::function<void()>>>
593 stopCallback = std::nullopt;
594};
595
596/** @class SendRecvMsgSender
597 *
598 * Represents the single message sender
599 *
600 * @tparam RequestInterface - Request class type
601 */
602template <class RequestInterface>
603struct SendRecvMsgSender
604{
605 using is_sender = void;
606
607 SendRecvMsgSender() = delete;
608
609 explicit SendRecvMsgSender(requester::Handler<RequestInterface>& handler,
610 mctp_eid_t eid, pldm::Request&& request) :
Patrick Williams16c2a0a2024-08-16 15:20:59 -0400611 handler(handler), eid(eid), request(std::move(request))
Gilbert Chena85c69d2024-01-09 21:25:26 +0700612 {}
613
614 friend auto tag_invoke(stdexec::get_completion_signatures_t,
615 const SendRecvMsgSender&, auto)
616 -> stdexec::completion_signatures<
Thu Nguyen6b901e42024-07-10 15:21:10 +0700617 stdexec::set_value_t(int, const pldm_msg*, size_t),
618 stdexec::set_stopped_t()>;
Gilbert Chena85c69d2024-01-09 21:25:26 +0700619
620 /** @brief Execute the sending the request message */
621 template <stdexec::receiver R>
622 friend auto tag_invoke(stdexec::connect_t, SendRecvMsgSender&& self, R r)
623 {
624 return SendRecvMsgOperation<RequestInterface, R>(
625 self.handler, self.eid, std::move(self.request), std::move(r));
626 }
627
628 private:
629 /** @brief Reference to a Handler object that manages the request/response
630 * logic.
631 */
632 requester::Handler<RequestInterface>& handler;
633
634 /** @brief MCTP Endpoint ID of request message */
635 mctp_eid_t eid;
636
637 /** @brief Request message */
638 pldm::Request request;
639};
640
Thu Nguyen6b901e42024-07-10 15:21:10 +0700641/** @brief Wrap registerRequest with coroutine API.
Gilbert Chena85c69d2024-01-09 21:25:26 +0700642 *
643 * @param[in] eid - endpoint ID of the remote MCTP endpoint
644 * @param[in] request - PLDM request message
645 *
Thu Nguyen6b901e42024-07-10 15:21:10 +0700646 * @return Return [PLDM_ERROR, _, _] if registerRequest fails.
647 * Return [PLDM_ERROR_NOT_READY, nullptr, 0] if timed out.
648 * Return [PLDM_SUCCESS, resp, len] if succeeded
Gilbert Chena85c69d2024-01-09 21:25:26 +0700649 */
650template <class RequestInterface>
Thu Nguyen6b901e42024-07-10 15:21:10 +0700651stdexec::sender_of<stdexec::set_value_t(SendRecvCoResp)> auto
Gilbert Chena85c69d2024-01-09 21:25:26 +0700652 Handler<RequestInterface>::sendRecvMsg(mctp_eid_t eid,
653 pldm::Request&& request)
654{
655 return SendRecvMsgSender(*this, eid, std::move(request)) |
Thu Nguyen6b901e42024-07-10 15:21:10 +0700656 stdexec::then([](int rc, const pldm_msg* resp, size_t respLen) {
Patrick Williams16c2a0a2024-08-16 15:20:59 -0400657 return std::make_tuple(rc, resp, respLen);
658 });
Gilbert Chena85c69d2024-01-09 21:25:26 +0700659}
660
Tom Joseph74f27c72021-05-16 07:58:53 -0700661} // namespace requester
662
663} // namespace pldm