blob: 465e34fba399c4f71cb11718508b908b16c701c9 [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 */
290 pollEndpointQueue(eid);
Tom Joseph74f27c72021-05-16 07:58:53 -0700291
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700292 return PLDM_SUCCESS;
Tom Joseph74f27c72021-05-16 07:58:53 -0700293 }
294
Gilbert Chena85c69d2024-01-09 21:25:26 +0700295 /** @brief Unregister a PLDM request message
296 *
297 * @param[in] eid - endpoint ID of the remote MCTP endpoint
298 * @param[in] instanceId - instance ID to match request and response
299 * @param[in] type - PLDM type
300 * @param[in] command - PLDM command
301 *
302 * @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise
303 */
304 int unregisterRequest(mctp_eid_t eid, uint8_t instanceId, uint8_t type,
305 uint8_t command)
306 {
307 RequestKey key{eid, instanceId, type, command};
308
309 /* handlers only contain key when the message is already sent */
310 if (handlers.contains(key))
311 {
312 auto& [request, responseHandler, timerInstance] = handlers[key];
313 request->stop();
314 auto rc = timerInstance->stop();
315 if (rc)
316 {
317 error(
318 "Failed to stop the instance ID expiry timer, response code '{RC}'",
319 "RC", static_cast<int>(rc));
320 }
321
322 instanceIdDb.free(key.eid, key.instanceId);
323 handlers.erase(key);
324 endpointMessageQueues[eid]->activeRequest = false;
325 /* try to send new request if the endpoint is free */
326 pollEndpointQueue(eid);
327
328 return PLDM_SUCCESS;
329 }
330 else
331 {
332 if (!endpointMessageQueues.contains(eid))
333 {
334 error(
335 "Can't find request for EID '{EID}' is using InstanceID '{INSTANCEID}' in Endpoint message Queue",
336 "EID", (unsigned)eid, "INSTANCEID", (unsigned)instanceId);
337 return PLDM_ERROR;
338 }
339 auto requestMsg = endpointMessageQueues[eid]->requestQueue;
340 /* Find the registered request in the requestQueue */
341 for (auto it = requestMsg.begin(); it != requestMsg.end();)
342 {
343 auto msg = *it;
344 if (msg->key == key)
345 {
346 // erase and get the next valid iterator
347 it = endpointMessageQueues[eid]->requestQueue.erase(it);
348 instanceIdDb.free(key.eid, key.instanceId);
349 return PLDM_SUCCESS;
350 }
351 else
352 {
353 ++it; // increment iterator only if not erasing
354 }
355 }
356 }
357
358 return PLDM_ERROR;
359 }
360
Tom Joseph74f27c72021-05-16 07:58:53 -0700361 /** @brief Handle PLDM response message
362 *
363 * @param[in] eid - endpoint ID of the remote MCTP endpoint
364 * @param[in] instanceId - instance ID to match request and response
365 * @param[in] type - PLDM type
366 * @param[in] command - PLDM command
367 * @param[in] response - PLDM response message
368 * @param[in] respMsgLen - length of the response message
369 */
370 void handleResponse(mctp_eid_t eid, uint8_t instanceId, uint8_t type,
371 uint8_t command, const pldm_msg* response,
372 size_t respMsgLen)
373 {
374 RequestKey key{eid, instanceId, type, command};
375 if (handlers.contains(key))
376 {
377 auto& [request, responseHandler, timerInstance] = handlers[key];
378 request->stop();
379 auto rc = timerInstance->stop();
380 if (rc)
381 {
Riya Dixit087a7512024-04-06 14:28:08 -0500382 error(
383 "Failed to stop the instance ID expiry timer, response code '{RC}'",
Riya Dixit1e5c81e2024-05-03 07:54:00 -0500384 "RC", rc);
Tom Joseph74f27c72021-05-16 07:58:53 -0700385 }
386 responseHandler(eid, response, respMsgLen);
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930387 instanceIdDb.free(key.eid, key.instanceId);
Tom Joseph74f27c72021-05-16 07:58:53 -0700388 handlers.erase(key);
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700389
390 endpointMessageQueues[eid]->activeRequest = false;
391 /* try to send new request if the endpoint is free */
392 pollEndpointQueue(eid);
Tom Joseph74f27c72021-05-16 07:58:53 -0700393 }
394 else
395 {
396 // Got a response for a PLDM request message not registered with the
397 // request handler, so freeing up the instance ID, this can be other
398 // OpenBMC applications relying on PLDM D-Bus apis like
399 // openpower-occ-control and softoff
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930400 instanceIdDb.free(key.eid, key.instanceId);
Tom Joseph74f27c72021-05-16 07:58:53 -0700401 }
402 }
403
Gilbert Chena85c69d2024-01-09 21:25:26 +0700404 /** @brief Wrap registerRequest with coroutine API.
405 *
Thu Nguyen6b901e42024-07-10 15:21:10 +0700406 * @return Return [PLDM_ERROR, _, _] if registerRequest fails.
407 * Return [PLDM_ERROR_NOT_READY, nullptr, 0] if timed out.
408 * Return [PLDM_SUCCESS, resp, len] if succeeded
Gilbert Chena85c69d2024-01-09 21:25:26 +0700409 */
Thu Nguyen6b901e42024-07-10 15:21:10 +0700410 stdexec::sender_of<stdexec::set_value_t(SendRecvCoResp)> auto
411 sendRecvMsg(mctp_eid_t eid, pldm::Request&& request);
Gilbert Chena85c69d2024-01-09 21:25:26 +0700412
Tom Joseph74f27c72021-05-16 07:58:53 -0700413 private:
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +1000414 PldmTransport* pldmTransport; //!< PLDM transport object
Brad Bishop5079ac42021-08-19 18:35:06 -0400415 sdeventplus::Event& event; //!< reference to PLDM daemon's main event loop
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930416 pldm::InstanceIdDb& instanceIdDb; //!< reference to an InstanceIdDb
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930417 bool verbose; //!< verbose tracing flag
Brad Bishop5079ac42021-08-19 18:35:06 -0400418 std::chrono::seconds
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930419 instanceIdExpiryInterval; //!< Instance ID expiration interval
420 uint8_t numRetries; //!< number of request retries
Brad Bishop5079ac42021-08-19 18:35:06 -0400421 std::chrono::milliseconds
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930422 responseTimeOut; //!< time to wait between each retry
Tom Joseph74f27c72021-05-16 07:58:53 -0700423
Tom Josephe5268cd2021-09-07 13:04:03 +0530424 /** @brief Container for storing the details of the PLDM request
425 * message, handler for the corresponding PLDM response and the
426 * timer object for the Instance ID expiration
Tom Joseph74f27c72021-05-16 07:58:53 -0700427 */
Brad Bishop5079ac42021-08-19 18:35:06 -0400428 using RequestValue =
429 std::tuple<std::unique_ptr<RequestInterface>, ResponseHandler,
Patrick Williams35535cf2023-12-05 12:45:02 -0600430 std::unique_ptr<sdbusplus::Timer>>;
Tom Joseph74f27c72021-05-16 07:58:53 -0700431
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700432 // Manage the requests of responders base on MCTP EID
433 std::map<mctp_eid_t, std::shared_ptr<EndpointMessageQueue>>
434 endpointMessageQueues;
435
Tom Joseph74f27c72021-05-16 07:58:53 -0700436 /** @brief Container for storing the PLDM request entries */
437 std::unordered_map<RequestKey, RequestValue, RequestKeyHasher> handlers;
438
439 /** @brief Container to store information about the request entries to be
440 * removed after the instance ID timer expires
441 */
Brad Bishop5079ac42021-08-19 18:35:06 -0400442 std::unordered_map<RequestKey, std::unique_ptr<sdeventplus::source::Defer>,
443 RequestKeyHasher>
Tom Joseph74f27c72021-05-16 07:58:53 -0700444 removeRequestContainer;
445
446 /** @brief Remove request entry for which the instance ID expired
447 *
448 * @param[in] key - key for the Request
449 */
450 void removeRequestEntry(RequestKey key)
451 {
452 if (removeRequestContainer.contains(key))
453 {
454 removeRequestContainer[key].reset();
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930455 instanceIdDb.free(key.eid, key.instanceId);
Tom Joseph74f27c72021-05-16 07:58:53 -0700456 handlers.erase(key);
457 removeRequestContainer.erase(key);
458 }
459 }
460};
461
Gilbert Chena85c69d2024-01-09 21:25:26 +0700462/** @class SendRecvMsgOperation
463 *
464 * Represents the state and logic for a single send/receive message operation
465 *
466 * @tparam RequestInterface - Request class type
467 * @tparam stdexec::receiver - Execute receiver
468 */
469template <class RequestInterface, stdexec::receiver R>
470struct SendRecvMsgOperation
471{
472 SendRecvMsgOperation() = delete;
473
474 explicit SendRecvMsgOperation(Handler<RequestInterface>& handler,
475 mctp_eid_t eid, pldm::Request&& request,
476 R&& r) :
Patrick Williams16c2a0a2024-08-16 15:20:59 -0400477 handler(handler), request(std::move(request)), receiver(std::move(r))
Gilbert Chena85c69d2024-01-09 21:25:26 +0700478 {
479 auto requestMsg =
480 reinterpret_cast<const pldm_msg*>(this->request.data());
481 requestKey = RequestKey{
482 eid,
483 requestMsg->hdr.instance_id,
484 requestMsg->hdr.type,
485 requestMsg->hdr.command,
486 };
487 response = nullptr;
488 respMsgLen = 0;
489 }
490
491 /** @brief Checks if the operation has been requested to stop.
492 * If so, it sets the state to stopped.Registers the request with
493 * the handler. If registration fails, sets an error on the
494 * receiver. If stopping is possible, sets up a stop callback.
495 *
496 * @param[in] op - operation request
497 *
498 * @return Execute errors
499 */
500 friend void tag_invoke(stdexec::start_t, SendRecvMsgOperation& op) noexcept
501 {
502 auto stopToken = stdexec::get_stop_token(stdexec::get_env(op.receiver));
503
504 // operation already cancelled
505 if (stopToken.stop_requested())
506 {
507 return stdexec::set_stopped(std::move(op.receiver));
508 }
509
510 using namespace std::placeholders;
511 auto rc = op.handler.registerRequest(
512 op.requestKey.eid, op.requestKey.instanceId, op.requestKey.type,
513 op.requestKey.command, std::move(op.request),
514 std::bind(&SendRecvMsgOperation::onComplete, &op, _1, _2, _3));
515 if (rc)
516 {
Thu Nguyen6b901e42024-07-10 15:21:10 +0700517 return stdexec::set_value(std::move(op.receiver), rc,
518 static_cast<const pldm_msg*>(nullptr),
519 static_cast<size_t>(0));
Gilbert Chena85c69d2024-01-09 21:25:26 +0700520 }
521
522 if (stopToken.stop_possible())
523 {
524 op.stopCallback.emplace(
525 std::move(stopToken),
526 std::bind(&SendRecvMsgOperation::onStop, &op));
527 }
528 }
529
530 /** @brief Unregisters the request and sets the state to stopped on the
531 * receiver.
532 */
533 void onStop()
534 {
535 handler.unregisterRequest(requestKey.eid, requestKey.instanceId,
536 requestKey.type, requestKey.command);
537 return stdexec::set_stopped(std::move(receiver));
538 }
539
540 /** @brief This function resets the stop callback. Validates the response
541 * and sets either an error or a value on the receiver.
542 *
543 * @param[in] eid - endpoint ID of the remote MCTP endpoint
544 * @param[in] response - PLDM response message
545 * @param[in] respMsgLen - length of the response message
546 *
547 * @return PLDM completion code
548 */
549 void onComplete(mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen)
550 {
551 stopCallback.reset();
552 assert(eid == this->requestKey.eid);
Thu Nguyen6b901e42024-07-10 15:21:10 +0700553 auto rc = PLDM_SUCCESS;
554 if (!response && !respMsgLen)
Gilbert Chena85c69d2024-01-09 21:25:26 +0700555 {
Thu Nguyen6b901e42024-07-10 15:21:10 +0700556 rc = PLDM_ERROR_NOT_READY;
Gilbert Chena85c69d2024-01-09 21:25:26 +0700557 }
Thu Nguyen6b901e42024-07-10 15:21:10 +0700558 return stdexec::set_value(std::move(receiver), static_cast<int>(rc),
559 response, respMsgLen);
Gilbert Chena85c69d2024-01-09 21:25:26 +0700560 }
561
562 private:
563 /** @brief Reference to a Handler object that manages the request/response
564 * logic.
565 */
566 requester::Handler<RequestInterface>& handler;
567
568 /** @brief Stores information about the request such as eid, instanceId,
569 * type, and command.
570 */
571 RequestKey requestKey;
572
573 /** @brief The request message to be sent.
574 */
575 pldm::Request request;
576
577 /** @brief The response message for the sent request message.
578 */
579 const pldm_msg* response;
580
581 /** @brief The length of response message for the sent request message.
582 */
583 size_t respMsgLen;
584
585 /** @brief The receiver to be notified with the result of the operation.
586 */
587 R receiver;
588
589 /** @brief An optional callback that handles stopping the operation if
590 * requested.
591 */
592 std::optional<typename stdexec::stop_token_of_t<
593 stdexec::env_of_t<R>>::template callback_type<std::function<void()>>>
594 stopCallback = std::nullopt;
595};
596
597/** @class SendRecvMsgSender
598 *
599 * Represents the single message sender
600 *
601 * @tparam RequestInterface - Request class type
602 */
603template <class RequestInterface>
604struct SendRecvMsgSender
605{
606 using is_sender = void;
607
608 SendRecvMsgSender() = delete;
609
610 explicit SendRecvMsgSender(requester::Handler<RequestInterface>& handler,
611 mctp_eid_t eid, pldm::Request&& request) :
Patrick Williams16c2a0a2024-08-16 15:20:59 -0400612 handler(handler), eid(eid), request(std::move(request))
Gilbert Chena85c69d2024-01-09 21:25:26 +0700613 {}
614
615 friend auto tag_invoke(stdexec::get_completion_signatures_t,
616 const SendRecvMsgSender&, auto)
617 -> stdexec::completion_signatures<
Thu Nguyen6b901e42024-07-10 15:21:10 +0700618 stdexec::set_value_t(int, const pldm_msg*, size_t),
619 stdexec::set_stopped_t()>;
Gilbert Chena85c69d2024-01-09 21:25:26 +0700620
621 /** @brief Execute the sending the request message */
622 template <stdexec::receiver R>
623 friend auto tag_invoke(stdexec::connect_t, SendRecvMsgSender&& self, R r)
624 {
625 return SendRecvMsgOperation<RequestInterface, R>(
626 self.handler, self.eid, std::move(self.request), std::move(r));
627 }
628
629 private:
630 /** @brief Reference to a Handler object that manages the request/response
631 * logic.
632 */
633 requester::Handler<RequestInterface>& handler;
634
635 /** @brief MCTP Endpoint ID of request message */
636 mctp_eid_t eid;
637
638 /** @brief Request message */
639 pldm::Request request;
640};
641
Thu Nguyen6b901e42024-07-10 15:21:10 +0700642/** @brief Wrap registerRequest with coroutine API.
Gilbert Chena85c69d2024-01-09 21:25:26 +0700643 *
644 * @param[in] eid - endpoint ID of the remote MCTP endpoint
645 * @param[in] request - PLDM request message
646 *
Thu Nguyen6b901e42024-07-10 15:21:10 +0700647 * @return Return [PLDM_ERROR, _, _] if registerRequest fails.
648 * Return [PLDM_ERROR_NOT_READY, nullptr, 0] if timed out.
649 * Return [PLDM_SUCCESS, resp, len] if succeeded
Gilbert Chena85c69d2024-01-09 21:25:26 +0700650 */
651template <class RequestInterface>
Thu Nguyen6b901e42024-07-10 15:21:10 +0700652stdexec::sender_of<stdexec::set_value_t(SendRecvCoResp)> auto
Gilbert Chena85c69d2024-01-09 21:25:26 +0700653 Handler<RequestInterface>::sendRecvMsg(mctp_eid_t eid,
654 pldm::Request&& request)
655{
656 return SendRecvMsgSender(*this, eid, std::move(request)) |
Thu Nguyen6b901e42024-07-10 15:21:10 +0700657 stdexec::then([](int rc, const pldm_msg* resp, size_t respLen) {
Patrick Williams16c2a0a2024-08-16 15:20:59 -0400658 return std::make_tuple(rc, resp, respLen);
659 });
Gilbert Chena85c69d2024-01-09 21:25:26 +0700660}
661
Tom Joseph74f27c72021-05-16 07:58:53 -0700662} // namespace requester
663
664} // namespace pldm