common: i2c: Add non-coroutine sendReceive()

This commit introduces a non-coroutine version of `sendReceive()` in the
common I2C module. The new implementation avoids potential stack buffer
issues by ensuring synchronous execution.

Change-Id: I1d308f24fafa6e0d68ffcc67073e0a2e67a6b419
Signed-off-by: Daniel Hsu <Daniel-Hsu@quantatw.com>
diff --git a/common/i2c/i2c.cpp b/common/i2c/i2c.cpp
index fb8bee0..653f823 100644
--- a/common/i2c/i2c.cpp
+++ b/common/i2c/i2c.cpp
@@ -76,6 +76,51 @@
     co_return result;
 }
 
+bool I2C::sendReceive(const std::vector<uint8_t>& writeData,
+                      std::vector<uint8_t>& readData) const
+{
+    bool result = true;
+
+    if (fd <= 0)
+    {
+        return false;
+    }
+    else
+    {
+        struct i2c_msg msg[2];
+        struct i2c_rdwr_ioctl_data readWriteData;
+        int msgIndex = 0;
+
+        if (!writeData.empty())
+        {
+            msg[msgIndex].addr = deviceNode;
+            msg[msgIndex].flags = 0;
+            msg[msgIndex].len = writeData.size();
+            msg[msgIndex].buf = const_cast<uint8_t*>(writeData.data());
+            msgIndex++;
+        }
+
+        if (!readData.empty())
+        {
+            msg[msgIndex].addr = deviceNode;
+            msg[msgIndex].flags = I2C_M_RD;
+            msg[msgIndex].len = readData.size();
+            msg[msgIndex].buf = readData.data();
+            msgIndex++;
+        }
+
+        readWriteData.msgs = msg;
+        readWriteData.nmsgs = msgIndex;
+
+        if (ioctl(fd, I2C_RDWR, &readWriteData) < 0)
+        {
+            result = false;
+        }
+    }
+
+    return result;
+}
+
 int I2C::close() const
 {
     return ::close(fd);
diff --git a/common/include/i2c/i2c.hpp b/common/include/i2c/i2c.hpp
index e86d574..d230ef7 100644
--- a/common/include/i2c/i2c.hpp
+++ b/common/include/i2c/i2c.hpp
@@ -42,6 +42,8 @@
     sdbusplus::async::task<bool> sendReceive(
         uint8_t* writeData, uint8_t writeSize, uint8_t* readData,
         uint8_t readSize) const;
+    bool sendReceive(const std::vector<uint8_t>& writeData,
+                     std::vector<uint8_t>& readData) const;
 
     bool isOpen() const
     {
diff --git a/cpld/cpld_software_manager.cpp b/cpld/cpld_software_manager.cpp
index faf14a5..6303135 100644
--- a/cpld/cpld_software_manager.cpp
+++ b/cpld/cpld_software_manager.cpp
@@ -16,17 +16,14 @@
     std::string configIface =
         "xyz.openbmc_project.Configuration." + config.configType;
 
-    std::optional<uint64_t> busNo = co_await dbusGetRequiredProperty<uint64_t>(
+    auto busNo = co_await dbusGetRequiredProperty<uint64_t>(
         ctx, service, path, configIface, "Bus");
-    std::optional<uint64_t> address =
-        co_await dbusGetRequiredProperty<uint64_t>(ctx, service, path,
-                                                   configIface, "Address");
-    std::optional<std::string> chipType =
-        co_await dbusGetRequiredProperty<std::string>(ctx, service, path,
-                                                      configIface, "Type");
-    std::optional<std::string> chipName =
-        co_await dbusGetRequiredProperty<std::string>(ctx, service, path,
-                                                      configIface, "Name");
+    auto address = co_await dbusGetRequiredProperty<uint64_t>(
+        ctx, service, path, configIface, "Address");
+    auto chipType = co_await dbusGetRequiredProperty<std::string>(
+        ctx, service, path, configIface, "Type");
+    auto chipName = co_await dbusGetRequiredProperty<std::string>(
+        ctx, service, path, configIface, "Name");
 
     if (!busNo.has_value() || !address.has_value() || !chipType.has_value() ||
         !chipName.has_value())
diff --git a/cpld/lattice/lattice.cpp b/cpld/lattice/lattice.cpp
index ce70fa3..48e2479 100644
--- a/cpld/lattice/lattice.cpp
+++ b/cpld/lattice/lattice.cpp
@@ -8,8 +8,6 @@
 #include <thread>
 #include <vector>
 
-using sdbusplus::async::details::context_friend;
-
 constexpr uint8_t busyWaitmaxRetry = 30;
 constexpr uint8_t busyFlagBit = 0x80;
 constexpr std::chrono::milliseconds waitBusyTime(200);
@@ -73,6 +71,14 @@
     return static_cast<int>(pos2 - pos1 - 1);
 }
 
+std::string uint32ToHexStr(uint32_t value)
+{
+    std::ostringstream oss;
+    oss << std::setfill('0') << std::setw(8) << std::hex << std::uppercase
+        << value;
+    return oss.str();
+}
+
 bool CpldLatticeManager::jedFileParser()
 {
     bool cfStart = false;
@@ -332,13 +338,12 @@
 
 sdbusplus::async::task<bool> CpldLatticeManager::readDeviceId()
 {
-    auto sched = context_friend::get_scheduler(ctx);
-    std::vector<uint8_t> command = {commandReadDeviceId, 0x0, 0x0, 0x0};
+    std::vector<uint8_t> request = {commandReadDeviceId, 0x0, 0x0, 0x0};
     constexpr size_t resSize = 4;
     std::vector<uint8_t> readData(resSize, 0);
-    bool success = co_await stdexec::starts_on(
-        sched, i2cInterface.sendReceive(command.data(), command.size(),
-                                        readData.data(), resSize));
+    bool success = co_await i2cInterface.sendReceive(
+        request.data(), request.size(), readData.data(), resSize);
+
     if (!success)
     {
         lg2::error(
@@ -377,14 +382,12 @@
 
 sdbusplus::async::task<bool> CpldLatticeManager::enableProgramMode()
 {
-    auto sched = context_friend::get_scheduler(ctx);
-    std::vector<uint8_t> command = {commandEnableConfigMode, 0x08, 0x0, 0x0};
-    bool success = co_await stdexec::starts_on(
-        sched,
-        i2cInterface.sendReceive(command.data(), command.size(), nullptr, 0));
+    std::vector<uint8_t> request = {commandEnableConfigMode, 0x08, 0x0, 0x0};
+    std::vector<uint8_t> response;
 
-    if (!success)
+    if (!i2cInterface.sendReceive(request, response))
     {
+        lg2::error("Failed to send enable program mode request.");
         co_return false;
     }
 
@@ -399,8 +402,8 @@
 
 sdbusplus::async::task<bool> CpldLatticeManager::eraseFlash()
 {
-    auto sched = context_friend::get_scheduler(ctx);
-    std::vector<uint8_t> command;
+    std::vector<uint8_t> request;
+    std::vector<uint8_t> response;
 
     if (isLCMXO3D)
     {
@@ -426,11 +429,11 @@
         */
         if (target.empty() || target == "CFG0")
         {
-            command = {commandEraseFlash, 0x00, 0x01, 0x00};
+            request = {commandEraseFlash, 0x00, 0x01, 0x00};
         }
         else if (target == "CFG1")
         {
-            command = {commandEraseFlash, 0x00, 0x02, 0x00};
+            request = {commandEraseFlash, 0x00, 0x02, 0x00};
         }
         else
         {
@@ -440,14 +443,12 @@
     }
     else
     {
-        command = {commandEraseFlash, 0xC, 0x0, 0x0};
+        request = {commandEraseFlash, 0xC, 0x0, 0x0};
     }
 
-    bool success = co_await stdexec::starts_on(
-        sched,
-        i2cInterface.sendReceive(command.data(), command.size(), nullptr, 0));
-    if (!success)
+    if (!i2cInterface.sendReceive(request, response))
     {
+        lg2::error("Failed to send erase flash request.");
         co_return false;
     }
 
@@ -462,8 +463,8 @@
 
 sdbusplus::async::task<bool> CpldLatticeManager::resetConfigFlash()
 {
-    auto sched = context_friend::get_scheduler(ctx);
-    std::vector<uint8_t> command;
+    std::vector<uint8_t> request;
+    std::vector<uint8_t> response;
     if (isLCMXO3D)
     {
         /*
@@ -491,11 +492,11 @@
         */
         if (target.empty() || target == "CFG0")
         {
-            command = {commandResetConfigFlash, 0x00, 0x01, 0x00};
+            request = {commandResetConfigFlash, 0x00, 0x01, 0x00};
         }
         else if (target == "CFG1")
         {
-            command = {commandResetConfigFlash, 0x00, 0x02, 0x00};
+            request = {commandResetConfigFlash, 0x00, 0x02, 0x00};
         }
         else
         {
@@ -506,12 +507,10 @@
     }
     else
     {
-        command = {commandResetConfigFlash, 0x0, 0x0, 0x0};
+        request = {commandResetConfigFlash, 0x0, 0x0, 0x0};
     }
 
-    co_return co_await stdexec::starts_on(
-        sched,
-        i2cInterface.sendReceive(command.data(), command.size(), nullptr, 0));
+    co_return i2cInterface.sendReceive(request, response);
 }
 
 sdbusplus::async::task<bool> CpldLatticeManager::writeProgramPage()
@@ -521,8 +520,8 @@
     used to program the NVCM0/CFG or
     NVCM1/UFM.
     */
-    auto sched = context_friend::get_scheduler(ctx);
-    std::vector<uint8_t> command = {commandProgramPage, 0x0, 0x0, 0x01};
+    std::vector<uint8_t> request = {commandProgramPage, 0x0, 0x0, 0x01};
+    std::vector<uint8_t> response;
     size_t iterSize = 16;
 
     for (size_t i = 0; i < fwInfo.cfgData.size(); i += iterSize)
@@ -535,17 +534,16 @@
         uint8_t len = ((i + iterSize) < fwInfo.cfgData.size())
                           ? iterSize
                           : (fwInfo.cfgData.size() - i);
-        std::vector<uint8_t> data = command;
+        std::vector<uint8_t> data = request;
 
         data.insert(
             data.end(), fwInfo.cfgData.begin() + static_cast<std::ptrdiff_t>(i),
             fwInfo.cfgData.begin() + static_cast<std::ptrdiff_t>(i + len));
 
-        bool success = co_await stdexec::starts_on(
-            sched,
-            i2cInterface.sendReceive(data.data(), data.size(), nullptr, 0));
-        if (!success)
+        if (!i2cInterface.sendReceive(data, response))
         {
+            lg2::error("Failed to send program page request. {CURRENT}",
+                       "CURRENT", uint32ToHexStr(i));
             co_return false;
         }
 
@@ -570,21 +568,18 @@
 
 sdbusplus::async::task<bool> CpldLatticeManager::programUserCode()
 {
-    auto sched = context_friend::get_scheduler(ctx);
-    std::vector<uint8_t> command = {commandProgramUserCode, 0x0, 0x0, 0x0};
+    std::vector<uint8_t> request = {commandProgramUserCode, 0x0, 0x0, 0x0};
+    std::vector<uint8_t> response;
     for (int i = 3; i >= 0; i--)
     {
-        command.push_back((fwInfo.version >> (i * 8)) & 0xFF);
+        request.push_back((fwInfo.version >> (i * 8)) & 0xFF);
     }
-    bool success = co_await stdexec::starts_on(
-        sched,
-        i2cInterface.sendReceive(command.data(), command.size(), nullptr, 0));
 
-    if (!success)
+    if (!i2cInterface.sendReceive(request, response))
     {
+        lg2::error("Failed to send program user code request.");
         co_return false;
     }
-
     if (!(co_await waitBusyAndVerify()))
     {
         lg2::error("Wait busy and verify fail");
@@ -596,16 +591,15 @@
 
 sdbusplus::async::task<bool> CpldLatticeManager::programDone()
 {
-    auto sched = context_friend::get_scheduler(ctx);
-    std::vector<uint8_t> command = {commandProgramDone, 0x0, 0x0, 0x0};
-    bool success = co_await stdexec::starts_on(
-        sched,
-        i2cInterface.sendReceive(command.data(), command.size(), nullptr, 0));
+    std::vector<uint8_t> request = {commandProgramDone, 0x0, 0x0, 0x0};
+    std::vector<uint8_t> response;
 
-    if (!success)
+    if (!i2cInterface.sendReceive(request, response))
     {
+        lg2::error("Failed to send program done request.");
         co_return false;
     }
+
     if (!(co_await waitBusyAndVerify()))
     {
         lg2::error("Wait busy and verify fail");
@@ -617,14 +611,9 @@
 
 sdbusplus::async::task<bool> CpldLatticeManager::disableConfigInterface()
 {
-    auto sched = context_friend::get_scheduler(ctx);
-    std::vector<uint8_t> command = {commandDisableConfigInterface, 0x0, 0x0};
-
-    bool success = co_await stdexec::starts_on(
-        sched,
-        i2cInterface.sendReceive(command.data(), command.size(), nullptr, 0));
-
-    co_return success;
+    std::vector<uint8_t> request = {commandDisableConfigInterface, 0x0, 0x0};
+    std::vector<uint8_t> response;
+    co_return i2cInterface.sendReceive(request, response);
 }
 
 sdbusplus::async::task<bool> CpldLatticeManager::waitBusyAndVerify()
@@ -635,7 +624,8 @@
     {
         uint8_t busyFlag = 0xff;
 
-        if (!(co_await readBusyFlag(busyFlag)))
+        auto readBusyFlagResult = co_await readBusyFlag(busyFlag);
+        if (!readBusyFlagResult)
         {
             lg2::error("Fail to read busy flag.");
             co_return false;
@@ -659,16 +649,16 @@
     } // while loop busy check
 
     // Check out status reg
-    uint8_t statusReg = 0xff;
+    auto statusReg = std::make_unique<uint8_t>(0xff);
 
-    if (!(co_await readStatusReg(statusReg)))
+    if (!(co_await readStatusReg(*statusReg)))
     {
         lg2::error("Fail to read status register.");
         co_return false;
     }
 
-    if (((statusReg >> busyOrReadyBit) & 1) == isReady &&
-        ((statusReg >> failOrOKBit) & 1) == isOK)
+    if (((*statusReg >> busyOrReadyBit) & 1) == isReady &&
+        ((*statusReg >> failOrOKBit) & 1) == isOK)
     {
         lg2::debug("Status Reg : OK");
         co_return true;
@@ -680,35 +670,28 @@
 
 sdbusplus::async::task<bool> CpldLatticeManager::readBusyFlag(uint8_t& busyFlag)
 {
-    auto sched = context_friend::get_scheduler(ctx);
-    std::vector<uint8_t> command = {commandReadBusyFlag, 0x0, 0x0, 0x0};
     constexpr size_t resSize = 1;
-    std::vector<uint8_t> readData(resSize, 0);
-    bool success = co_await stdexec::starts_on(
-        sched, i2cInterface.sendReceive(command.data(), command.size(),
-                                        readData.data(), resSize));
+    std::vector<uint8_t> request = {commandReadBusyFlag, 0x0, 0x0, 0x0};
+    std::vector<uint8_t> response(resSize, 0);
 
-    if (!success || (readData.size() != resSize))
+    auto success = i2cInterface.sendReceive(request, response);
+    if (!success && response.size() != resSize)
     {
         co_return false;
     }
-    busyFlag = readData.at(0);
+    busyFlag = response.at(0);
     co_return true;
 }
 
 sdbusplus::async::task<bool> CpldLatticeManager::readStatusReg(
     uint8_t& statusReg)
 {
-    auto sched = context_friend::get_scheduler(ctx);
-    std::vector<uint8_t> command = {commandReadStatusReg, 0x0, 0x0, 0x0};
-    constexpr size_t resSize = 4;
-    std::vector<uint8_t> readData(resSize, 0);
-    bool success = co_await stdexec::starts_on(
-        sched, i2cInterface.sendReceive(command.data(), command.size(),
-                                        readData.data(), resSize));
+    std::vector<uint8_t> request = {commandReadStatusReg, 0x0, 0x0, 0x0};
+    std::vector<uint8_t> response(4, 0);
 
-    if (!success || (readData.size() != resSize))
+    if (!i2cInterface.sendReceive(request, response))
     {
+        lg2::error("Failed to send read status register request.");
         co_return false;
     }
     /*
@@ -717,30 +700,27 @@
     0x3C 00 00 00 N/A YY YY YY YY Bit 1 0
     12 Busy Ready
     13 Fail OK
-        */
-    statusReg = readData.at(2);
+    */
+    statusReg = response.at(2);
     co_return true;
 }
 
 sdbusplus::async::task<bool> CpldLatticeManager::readUserCode(
     uint32_t& userCode)
 {
-    auto sched = context_friend::get_scheduler(ctx);
-    std::vector<uint8_t> command = {commandReadFwVersion, 0x0, 0x0, 0x0};
     constexpr size_t resSize = 4;
-    std::vector<uint8_t> readData(resSize, 0);
-    bool success = co_await stdexec::starts_on(
-        sched, i2cInterface.sendReceive(command.data(), command.size(),
-                                        readData.data(), resSize));
+    std::vector<uint8_t> request = {commandReadFwVersion, 0x0, 0x0, 0x0};
+    std::vector<uint8_t> response(resSize, 0);
 
-    if (!success)
+    if (!i2cInterface.sendReceive(request, response))
     {
+        lg2::error("Failed to send read user code request.");
         co_return false;
     }
 
     for (size_t i = 0; i < resSize; i++)
     {
-        userCode |= readData.at(i) << ((3 - i) * 8);
+        userCode |= response.at(i) << ((3 - i) * 8);
     }
     co_return true;
 }
@@ -856,28 +836,20 @@
     co_return false;
 }
 
-std::string uint32ToHexStr(uint32_t value)
-{
-    std::ostringstream oss;
-    oss << std::setfill('0') << std::setw(8) << std::hex << std::uppercase
-        << value;
-    return oss.str();
-}
-
 sdbusplus::async::task<bool> CpldLatticeManager::getVersion(
     std::string& version)
 {
-    uint32_t userCode = 0;
+    auto userCode = std::make_unique<uint32_t>(0);
 
     if (target.empty())
     {
-        if (!(co_await readUserCode(userCode)))
+        if (!(co_await readUserCode(*userCode)))
         {
             lg2::error("Read usercode failed.");
             co_return false;
         }
 
-        lg2::debug("CPLD version: {VERSION}", "VERSION", userCode);
+        lg2::debug("CPLD version: {VERSION}", "VERSION", *userCode);
     }
     else if (target == "CFG0" || target == "CFG1")
     {
@@ -896,7 +868,7 @@
             co_return false;
         }
 
-        if (!(co_await readUserCode(userCode)))
+        if (!(co_await readUserCode(*userCode)))
         {
             lg2::error("Read usercode failed.");
             co_return false;
@@ -915,7 +887,7 @@
         }
 
         lg2::debug("CPLD {TARGET} version: {VERSION}", "TARGET", target,
-                   "VERSION", userCode);
+                   "VERSION", *userCode);
     }
     else
     {
@@ -923,11 +895,11 @@
         co_return false;
     }
 
-    if (userCode == 0)
+    if (*userCode == 0)
     {
         lg2::error("User code is zero, cannot get version.");
         co_return false;
     }
-    version = uint32ToHexStr(userCode);
+    version = uint32ToHexStr(*userCode);
     co_return true;
 }