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 using namespace std::chrono; 22 23 /** @class RequestRetryTimer 24 * 25 * The abstract base class for implementing the PLDM request retry logic. This 26 * class handles number of times the PLDM request needs to be retried if the 27 * response is not received and the time to wait between each retry. It 28 * provides APIs to start and stop the request flow. 29 */ 30 class RequestRetryTimer 31 { 32 public: 33 RequestRetryTimer() = delete; 34 RequestRetryTimer(const RequestRetryTimer&) = delete; 35 RequestRetryTimer(RequestRetryTimer&&) = default; 36 RequestRetryTimer& operator=(const RequestRetryTimer&) = delete; 37 RequestRetryTimer& operator=(RequestRetryTimer&&) = default; 38 virtual ~RequestRetryTimer() = default; 39 40 /** @brief Constructor 41 * 42 * @param[in] event - reference to PLDM daemon's main event loop 43 * @param[in] numRetries - number of request retries 44 * @param[in] timeout - time to wait between each retry in milliseconds 45 */ 46 explicit RequestRetryTimer(sdeventplus::Event& event, uint8_t numRetries, 47 milliseconds timeout) : 48 49 event(event), 50 numRetries(numRetries), timeout(timeout), 51 timer(event.get(), std::bind_front(&RequestRetryTimer::callback, this)) 52 {} 53 54 /** @brief Starts the request flow and arms the timer for request retries 55 * 56 * @return return PLDM_SUCCESS on success and PLDM_ERROR otherwise 57 */ 58 int start() 59 { 60 auto rc = send(); 61 if (rc) 62 { 63 return rc; 64 } 65 66 try 67 { 68 if (numRetries) 69 { 70 timer.start(duration_cast<microseconds>(timeout), 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 milliseconds 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 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