blob: 36772435a828e10c2ff949ec22fb06f82f86a8b9 [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 }
401 else
402 {
403 // Got a response for a PLDM request message not registered with the
404 // request handler, so freeing up the instance ID, this can be other
405 // OpenBMC applications relying on PLDM D-Bus apis like
406 // openpower-occ-control and softoff
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930407 instanceIdDb.free(key.eid, key.instanceId);
Tom Joseph74f27c72021-05-16 07:58:53 -0700408 }
409 }
410
Gilbert Chena85c69d2024-01-09 21:25:26 +0700411 /** @brief Wrap registerRequest with coroutine API.
412 *
Thu Nguyen6b901e42024-07-10 15:21:10 +0700413 * @return Return [PLDM_ERROR, _, _] if registerRequest fails.
414 * Return [PLDM_ERROR_NOT_READY, nullptr, 0] if timed out.
415 * Return [PLDM_SUCCESS, resp, len] if succeeded
Gilbert Chena85c69d2024-01-09 21:25:26 +0700416 */
Patrick Williams366507c2025-02-03 14:28:01 -0500417 stdexec::sender_of<stdexec::set_value_t(SendRecvCoResp)> auto sendRecvMsg(
418 mctp_eid_t eid, pldm::Request&& request);
Gilbert Chena85c69d2024-01-09 21:25:26 +0700419
Tom Joseph74f27c72021-05-16 07:58:53 -0700420 private:
Rashmica Gupta1ed5f7a2023-05-22 13:56:42 +1000421 PldmTransport* pldmTransport; //!< PLDM transport object
Brad Bishop5079ac42021-08-19 18:35:06 -0400422 sdeventplus::Event& event; //!< reference to PLDM daemon's main event loop
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930423 pldm::InstanceIdDb& instanceIdDb; //!< reference to an InstanceIdDb
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930424 bool verbose; //!< verbose tracing flag
Brad Bishop5079ac42021-08-19 18:35:06 -0400425 std::chrono::seconds
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930426 instanceIdExpiryInterval; //!< Instance ID expiration interval
427 uint8_t numRetries; //!< number of request retries
Brad Bishop5079ac42021-08-19 18:35:06 -0400428 std::chrono::milliseconds
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930429 responseTimeOut; //!< time to wait between each retry
Tom Joseph74f27c72021-05-16 07:58:53 -0700430
Tom Josephe5268cd2021-09-07 13:04:03 +0530431 /** @brief Container for storing the details of the PLDM request
432 * message, handler for the corresponding PLDM response and the
433 * timer object for the Instance ID expiration
Tom Joseph74f27c72021-05-16 07:58:53 -0700434 */
Brad Bishop5079ac42021-08-19 18:35:06 -0400435 using RequestValue =
436 std::tuple<std::unique_ptr<RequestInterface>, ResponseHandler,
Patrick Williams35535cf2023-12-05 12:45:02 -0600437 std::unique_ptr<sdbusplus::Timer>>;
Tom Joseph74f27c72021-05-16 07:58:53 -0700438
Thu Nguyen4ddee3a2023-08-03 08:20:03 +0700439 // Manage the requests of responders base on MCTP EID
440 std::map<mctp_eid_t, std::shared_ptr<EndpointMessageQueue>>
441 endpointMessageQueues;
442
Tom Joseph74f27c72021-05-16 07:58:53 -0700443 /** @brief Container for storing the PLDM request entries */
444 std::unordered_map<RequestKey, RequestValue, RequestKeyHasher> handlers;
445
446 /** @brief Container to store information about the request entries to be
447 * removed after the instance ID timer expires
448 */
Brad Bishop5079ac42021-08-19 18:35:06 -0400449 std::unordered_map<RequestKey, std::unique_ptr<sdeventplus::source::Defer>,
450 RequestKeyHasher>
Tom Joseph74f27c72021-05-16 07:58:53 -0700451 removeRequestContainer;
452
453 /** @brief Remove request entry for which the instance ID expired
454 *
455 * @param[in] key - key for the Request
456 */
457 void removeRequestEntry(RequestKey key)
458 {
459 if (removeRequestContainer.contains(key))
460 {
461 removeRequestContainer[key].reset();
Andrew Jefferya330b2f2023-05-04 14:55:37 +0930462 instanceIdDb.free(key.eid, key.instanceId);
Tom Joseph74f27c72021-05-16 07:58:53 -0700463 handlers.erase(key);
464 removeRequestContainer.erase(key);
465 }
466 }
467};
468
Gilbert Chena85c69d2024-01-09 21:25:26 +0700469/** @class SendRecvMsgOperation
470 *
471 * Represents the state and logic for a single send/receive message operation
472 *
473 * @tparam RequestInterface - Request class type
474 * @tparam stdexec::receiver - Execute receiver
475 */
476template <class RequestInterface, stdexec::receiver R>
477struct SendRecvMsgOperation
478{
479 SendRecvMsgOperation() = delete;
480
481 explicit SendRecvMsgOperation(Handler<RequestInterface>& handler,
482 mctp_eid_t eid, pldm::Request&& request,
483 R&& r) :
Patrick Williams16c2a0a2024-08-16 15:20:59 -0400484 handler(handler), request(std::move(request)), receiver(std::move(r))
Gilbert Chena85c69d2024-01-09 21:25:26 +0700485 {
486 auto requestMsg =
487 reinterpret_cast<const pldm_msg*>(this->request.data());
488 requestKey = RequestKey{
489 eid,
490 requestMsg->hdr.instance_id,
491 requestMsg->hdr.type,
492 requestMsg->hdr.command,
493 };
494 response = nullptr;
495 respMsgLen = 0;
496 }
497
498 /** @brief Checks if the operation has been requested to stop.
499 * If so, it sets the state to stopped.Registers the request with
500 * the handler. If registration fails, sets an error on the
501 * receiver. If stopping is possible, sets up a stop callback.
502 *
503 * @param[in] op - operation request
504 *
505 * @return Execute errors
506 */
507 friend void tag_invoke(stdexec::start_t, SendRecvMsgOperation& op) noexcept
508 {
509 auto stopToken = stdexec::get_stop_token(stdexec::get_env(op.receiver));
510
511 // operation already cancelled
512 if (stopToken.stop_requested())
513 {
514 return stdexec::set_stopped(std::move(op.receiver));
515 }
516
517 using namespace std::placeholders;
518 auto rc = op.handler.registerRequest(
519 op.requestKey.eid, op.requestKey.instanceId, op.requestKey.type,
520 op.requestKey.command, std::move(op.request),
521 std::bind(&SendRecvMsgOperation::onComplete, &op, _1, _2, _3));
522 if (rc)
523 {
Thu Nguyen6b901e42024-07-10 15:21:10 +0700524 return stdexec::set_value(std::move(op.receiver), rc,
525 static_cast<const pldm_msg*>(nullptr),
526 static_cast<size_t>(0));
Gilbert Chena85c69d2024-01-09 21:25:26 +0700527 }
528
529 if (stopToken.stop_possible())
530 {
531 op.stopCallback.emplace(
532 std::move(stopToken),
533 std::bind(&SendRecvMsgOperation::onStop, &op));
534 }
535 }
536
537 /** @brief Unregisters the request and sets the state to stopped on the
538 * receiver.
539 */
540 void onStop()
541 {
542 handler.unregisterRequest(requestKey.eid, requestKey.instanceId,
543 requestKey.type, requestKey.command);
544 return stdexec::set_stopped(std::move(receiver));
545 }
546
547 /** @brief This function resets the stop callback. Validates the response
548 * and sets either an error or a value on the receiver.
549 *
550 * @param[in] eid - endpoint ID of the remote MCTP endpoint
551 * @param[in] response - PLDM response message
552 * @param[in] respMsgLen - length of the response message
553 *
554 * @return PLDM completion code
555 */
556 void onComplete(mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen)
557 {
558 stopCallback.reset();
559 assert(eid == this->requestKey.eid);
Thu Nguyen6b901e42024-07-10 15:21:10 +0700560 auto rc = PLDM_SUCCESS;
561 if (!response && !respMsgLen)
Gilbert Chena85c69d2024-01-09 21:25:26 +0700562 {
Thu Nguyen6b901e42024-07-10 15:21:10 +0700563 rc = PLDM_ERROR_NOT_READY;
Gilbert Chena85c69d2024-01-09 21:25:26 +0700564 }
Thu Nguyen6b901e42024-07-10 15:21:10 +0700565 return stdexec::set_value(std::move(receiver), static_cast<int>(rc),
566 response, respMsgLen);
Gilbert Chena85c69d2024-01-09 21:25:26 +0700567 }
568
569 private:
570 /** @brief Reference to a Handler object that manages the request/response
571 * logic.
572 */
573 requester::Handler<RequestInterface>& handler;
574
575 /** @brief Stores information about the request such as eid, instanceId,
576 * type, and command.
577 */
578 RequestKey requestKey;
579
580 /** @brief The request message to be sent.
581 */
582 pldm::Request request;
583
584 /** @brief The response message for the sent request message.
585 */
586 const pldm_msg* response;
587
588 /** @brief The length of response message for the sent request message.
589 */
590 size_t respMsgLen;
591
592 /** @brief The receiver to be notified with the result of the operation.
593 */
594 R receiver;
595
596 /** @brief An optional callback that handles stopping the operation if
597 * requested.
598 */
599 std::optional<typename stdexec::stop_token_of_t<
600 stdexec::env_of_t<R>>::template callback_type<std::function<void()>>>
601 stopCallback = std::nullopt;
602};
603
604/** @class SendRecvMsgSender
605 *
606 * Represents the single message sender
607 *
608 * @tparam RequestInterface - Request class type
609 */
610template <class RequestInterface>
611struct SendRecvMsgSender
612{
613 using is_sender = void;
614
615 SendRecvMsgSender() = delete;
616
617 explicit SendRecvMsgSender(requester::Handler<RequestInterface>& handler,
618 mctp_eid_t eid, pldm::Request&& request) :
Patrick Williams16c2a0a2024-08-16 15:20:59 -0400619 handler(handler), eid(eid), request(std::move(request))
Gilbert Chena85c69d2024-01-09 21:25:26 +0700620 {}
621
622 friend auto tag_invoke(stdexec::get_completion_signatures_t,
623 const SendRecvMsgSender&, auto)
624 -> stdexec::completion_signatures<
Thu Nguyen6b901e42024-07-10 15:21:10 +0700625 stdexec::set_value_t(int, const pldm_msg*, size_t),
626 stdexec::set_stopped_t()>;
Gilbert Chena85c69d2024-01-09 21:25:26 +0700627
628 /** @brief Execute the sending the request message */
629 template <stdexec::receiver R>
630 friend auto tag_invoke(stdexec::connect_t, SendRecvMsgSender&& self, R r)
631 {
632 return SendRecvMsgOperation<RequestInterface, R>(
633 self.handler, self.eid, std::move(self.request), std::move(r));
634 }
635
636 private:
637 /** @brief Reference to a Handler object that manages the request/response
638 * logic.
639 */
640 requester::Handler<RequestInterface>& handler;
641
642 /** @brief MCTP Endpoint ID of request message */
643 mctp_eid_t eid;
644
645 /** @brief Request message */
646 pldm::Request request;
647};
648
Thu Nguyen6b901e42024-07-10 15:21:10 +0700649/** @brief Wrap registerRequest with coroutine API.
Gilbert Chena85c69d2024-01-09 21:25:26 +0700650 *
651 * @param[in] eid - endpoint ID of the remote MCTP endpoint
652 * @param[in] request - PLDM request message
653 *
Thu Nguyen6b901e42024-07-10 15:21:10 +0700654 * @return Return [PLDM_ERROR, _, _] if registerRequest fails.
655 * Return [PLDM_ERROR_NOT_READY, nullptr, 0] if timed out.
656 * Return [PLDM_SUCCESS, resp, len] if succeeded
Gilbert Chena85c69d2024-01-09 21:25:26 +0700657 */
658template <class RequestInterface>
Thu Nguyen6b901e42024-07-10 15:21:10 +0700659stdexec::sender_of<stdexec::set_value_t(SendRecvCoResp)> auto
Gilbert Chena85c69d2024-01-09 21:25:26 +0700660 Handler<RequestInterface>::sendRecvMsg(mctp_eid_t eid,
661 pldm::Request&& request)
662{
663 return SendRecvMsgSender(*this, eid, std::move(request)) |
Thu Nguyen6b901e42024-07-10 15:21:10 +0700664 stdexec::then([](int rc, const pldm_msg* resp, size_t respLen) {
Patrick Williams16c2a0a2024-08-16 15:20:59 -0400665 return std::make_tuple(rc, resp, respLen);
666 });
Gilbert Chena85c69d2024-01-09 21:25:26 +0700667}
668
Tom Joseph74f27c72021-05-16 07:58:53 -0700669} // namespace requester
670
671} // namespace pldm