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