1 #pragma once 2 3 #include "libpldm/base.h" 4 #include "libpldm/requester/pldm.h" 5 6 #include "common/types.hpp" 7 8 #include <sdbusplus/timer.hpp> 9 #include <sdeventplus/event.hpp> 10 11 #include <chrono> 12 #include <functional> 13 #include <iostream> 14 15 namespace pldm 16 { 17 18 namespace requester 19 { 20 21 /** @class RequestRetryTimer 22 * 23 * The abstract base class for implementing the PLDM request retry logic. This 24 * class handles number of times the PLDM request needs to be retried if the 25 * response is not received and the time to wait between each retry. It 26 * provides APIs to start and stop the request flow. 27 */ 28 class RequestRetryTimer 29 { 30 public: 31 RequestRetryTimer() = delete; 32 RequestRetryTimer(const RequestRetryTimer&) = delete; 33 RequestRetryTimer(RequestRetryTimer&&) = default; 34 RequestRetryTimer& operator=(const RequestRetryTimer&) = delete; 35 RequestRetryTimer& operator=(RequestRetryTimer&&) = default; 36 virtual ~RequestRetryTimer() = default; 37 38 /** @brief Constructor 39 * 40 * @param[in] event - reference to PLDM daemon's main event loop 41 * @param[in] numRetries - number of request retries 42 * @param[in] timeout - time to wait between each retry in milliseconds 43 */ 44 explicit RequestRetryTimer(sdeventplus::Event& event, uint8_t numRetries, 45 std::chrono::milliseconds timeout) : 46 47 event(event), 48 numRetries(numRetries), timeout(timeout), 49 timer(event.get(), std::bind_front(&RequestRetryTimer::callback, this)) 50 {} 51 52 /** @brief Starts the request flow and arms the timer for request retries 53 * 54 * @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise 55 */ 56 int start() 57 { 58 auto rc = send(); 59 if (rc) 60 { 61 return rc; 62 } 63 64 try 65 { 66 if (numRetries) 67 { 68 timer.start(duration_cast<std::chrono::microseconds>(timeout), 69 true); 70 } 71 } 72 catch (const std::runtime_error& e) 73 { 74 std::cerr << "Failed to start the request timer. RC = " << e.what() 75 << "\n"; 76 return PLDM_ERROR; 77 } 78 79 return PLDM_SUCCESS; 80 } 81 82 /** @brief Stops the timer and no further request retries happen */ 83 void stop() 84 { 85 auto rc = timer.stop(); 86 if (rc) 87 { 88 std::cerr << "Failed to stop the request timer. RC = " << rc 89 << "\n"; 90 } 91 } 92 93 protected: 94 sdeventplus::Event& event; //!< reference to PLDM daemon's main event loop 95 uint8_t numRetries; //!< number of request retries 96 std::chrono::milliseconds 97 timeout; //!< time to wait between each retry in milliseconds 98 phosphor::Timer timer; //!< manages starting timers and handling timeouts 99 100 /** @brief Sends the PLDM request message 101 * 102 * @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise 103 */ 104 virtual int send() const = 0; 105 106 /** @brief Callback function invoked when the timeout happens */ 107 void callback() 108 { 109 if (numRetries--) 110 { 111 send(); 112 } 113 else 114 { 115 stop(); 116 } 117 } 118 }; 119 120 /** @class Request 121 * 122 * The concrete implementation of RequestIntf. This class implements the send() 123 * to send the PLDM request message over MCTP socket. 124 * This class encapsulates the PLDM request message, the number of times the 125 * request needs to retried if the response is not received and the amount of 126 * time to wait between each retry. It provides APIs to start and stop the 127 * request flow. 128 */ 129 class Request final : public RequestRetryTimer 130 { 131 public: 132 Request() = delete; 133 Request(const Request&) = delete; 134 Request(Request&&) = default; 135 Request& operator=(const Request&) = delete; 136 Request& operator=(Request&&) = default; 137 ~Request() = default; 138 139 /** @brief Constructor 140 * 141 * @param[in] fd - fd of the MCTP communication socket 142 * @param[in] eid - endpoint ID of the remote MCTP endpoint 143 * @param[in] event - reference to PLDM daemon's main event loop 144 * @param[in] requestMsg - PLDM request message 145 * @param[in] numRetries - number of request retries 146 * @param[in] timeout - time to wait between each retry in milliseconds 147 */ 148 explicit Request(int fd, mctp_eid_t eid, sdeventplus::Event& event, 149 pldm::Request&& requestMsg, uint8_t numRetries, 150 std::chrono::milliseconds timeout) : 151 RequestRetryTimer(event, numRetries, timeout), 152 fd(fd), eid(eid), requestMsg(std::move(requestMsg)) 153 {} 154 155 private: 156 int fd; //!< file descriptor of MCTP communications socket 157 mctp_eid_t eid; //!< endpoint ID of the remote MCTP endpoint 158 pldm::Request requestMsg; //!< PLDM request message 159 160 /** @brief Sends the PLDM request message on the socket 161 * 162 * @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise 163 */ 164 int send() const 165 { 166 auto rc = pldm_send(eid, fd, requestMsg.data(), requestMsg.size()); 167 if (rc < 0) 168 { 169 std::cerr << "Failed to send PLDM message. RC = " << rc 170 << ", errno = " << errno << "\n"; 171 return PLDM_ERROR; 172 } 173 return PLDM_SUCCESS; 174 } 175 }; 176 177 } // namespace requester 178 179 } // namespace pldm 180