i2c: move over to using std::expected for reads

Moves over to using std::expected for reads. This was mostly
an experiment, but I ended up testing and liking it more
than our out-value implementation. Put it up and make the world
a slightly better place.

Tested: put onto nvl32-obmc, i2c ready still works

Change-Id: I637cee5175fc684b6d02096a525b61ab6f4ab918
Signed-off-by: Marc Olberding <molberding@nvidia.com>
diff --git a/i2c.cpp b/i2c.cpp
index 0ad97fc..d33b00b 100644
--- a/i2c.cpp
+++ b/i2c.cpp
@@ -109,16 +109,16 @@
     close(fd);
 }
 
-int RawDevice::read_byte(uint8_t reg, uint8_t& val)
+std::expected<uint8_t, std::error_code> RawDevice::read_byte(uint8_t reg)
 {
     int result = i2c_smbus_read_byte_data(fd, reg);
     if (result < 0)
     {
-        return -result;
+        return std::unexpected(
+            std::error_code(-result, std::system_category()));
     }
 
-    val = result;
-    return 0;
+    return result;
 }
 
 } // namespace i2c
diff --git a/i2c.hpp b/i2c.hpp
index 4658f33..8491766 100644
--- a/i2c.hpp
+++ b/i2c.hpp
@@ -4,7 +4,9 @@
 #pragma once
 
 #include <cstdint>
+#include <expected>
 #include <string>
+#include <system_error>
 
 namespace i2c
 {
@@ -23,7 +25,7 @@
     RawDevice& operator=(RawDevice&&) = default;
     RawDevice(RawDevice&&) = default;
 
-    int read_byte(uint8_t reg, uint8_t& val);
+    std::expected<uint8_t, std::error_code> read_byte(uint8_t reg);
 
     int fd;
 };
diff --git a/nvidia/nvl32.cpp b/nvidia/nvl32.cpp
index 12f0a94..ddd5871 100644
--- a/nvidia/nvl32.cpp
+++ b/nvidia/nvl32.cpp
@@ -7,6 +7,7 @@
 #include <sdbusplus/asio/connection.hpp>
 
 #include <chrono>
+#include <expected>
 #include <filesystem>
 #include <fstream>
 #include <iostream>
@@ -59,20 +60,20 @@
     while (steady_clock::now() < end)
     {
         static constexpr uint8_t i2c_ready = 0xf2;
-        uint8_t result;
-        int rc = cpld.read_byte(i2c_ready, result);
-        if (rc)
-        {
-            std::string err =
-                std::format("Unable to communicate with cpld. rc: {}\n", rc);
-            std::cerr << err;
-            throw std::runtime_error(err);
-        }
+        const auto result = cpld.read_byte(i2c_ready);
 
-        if (result == 1)
+        if (result.has_value() && *result == 1)
         {
             return;
         }
+        else if (result.error())
+        {
+            std::string err =
+                std::format("Unable to communicate with cpld. rc: {}\n",
+                            result.error().value());
+            std::cerr << err;
+            throw std::runtime_error(err);
+        }
 
         std::this_thread::sleep_for(std::chrono::seconds{10});
     }