pw_sys_io: Add TryReadByte(byte* dest) function

This adds a non-blocking version of ReadByte() that enables writing, for
example, a superloop that combines custom processing simultaneously with
byte input handling for the RPC subsystem.

Change-Id: I9cb1e6ae8c944ca7cdff6d4bff101ac52e80b53a
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/23980
Reviewed-by: Armando Montanez <amontanez@google.com>
Commit-Queue: Keir Mierle <keir@google.com>
diff --git a/pw_sys_io/public/pw_sys_io/sys_io.h b/pw_sys_io/public/pw_sys_io/sys_io.h
index 5973579..477c719 100644
--- a/pw_sys_io/public/pw_sys_io/sys_io.h
+++ b/pw_sys_io/public/pw_sys_io/sys_io.h
@@ -51,9 +51,18 @@
 // This function will block until it either succeeds or fails to read a byte
 // from the pw_sys_io backend.
 //
-// Returns Status::Ok() if a byte was successfully read.
+// Returns Status::Ok() - A byte was successfully read.
+//         Status::ResourceExhausted() - if the underlying source vanished.
 Status ReadByte(std::byte* dest);
 
+// Read a single byte from the sys io backend, if available.
+// Implemented by: Backend
+//
+// Returns Status::Ok() - A byte was successfully read, and is in dest.
+//         Status::Unavailable() - No byte is available to read; try later.
+//         Status::Unimplemented() - Not supported on this target.
+Status TryReadByte(std::byte* dest);
+
 // Write a single byte out the sys io backend.
 // Implemented by: Backend
 //
diff --git a/pw_sys_io_arduino/sys_io_arduino.cc b/pw_sys_io_arduino/sys_io_arduino.cc
index c06f3a9..de6c344 100644
--- a/pw_sys_io_arduino/sys_io_arduino.cc
+++ b/pw_sys_io_arduino/sys_io_arduino.cc
@@ -35,11 +35,17 @@
 
 Status ReadByte(std::byte* dest) {
   while (true) {
-    if (Serial.available()) {
-      *dest = static_cast<std::byte>(Serial.read());
-      break;
+    if (TryReadByte(dest).ok()) {
+      return Status::Ok();
     }
   }
+}
+
+Status TryReadByte(std::byte* dest) {
+  if (!Serial.available()) {
+    return Status::Unavailable();
+  }
+  *dest = static_cast<std::byte>(Serial.read());
   return Status::Ok();
 }
 
diff --git a/pw_sys_io_baremetal_lm3s6965evb/sys_io_baremetal.cc b/pw_sys_io_baremetal_lm3s6965evb/sys_io_baremetal.cc
index ddf901e..86cc341 100644
--- a/pw_sys_io_baremetal_lm3s6965evb/sys_io_baremetal.cc
+++ b/pw_sys_io_baremetal_lm3s6965evb/sys_io_baremetal.cc
@@ -90,15 +90,21 @@
 // see if a byte is ready yet.
 Status ReadByte(std::byte* dest) {
   while (true) {
-    if (uart0.receive_error) {
-      // Writing anything to this register clears all errors.
-      uart0.receive_error = 0xff;
-    }
-    if (uart0.status_flags & kRxFifoFullMask) {
-      *dest = static_cast<std::byte>(uart0.data_register);
-      break;
+    if (TryReadByte(dest).ok()) {
+      return Status::Ok();
     }
   }
+}
+
+Status TryReadByte(std::byte* dest) {
+  if (uart0.receive_error) {
+    // Writing anything to this register clears all errors.
+    uart0.receive_error = 0xff;
+  }
+  if (!(uart0.status_flags & kRxFifoFullMask)) {
+    return Status::Unavailable();
+  }
+  *dest = static_cast<std::byte>(uart0.data_register);
   return Status::Ok();
 }
 
diff --git a/pw_sys_io_baremetal_stm32f429/sys_io_baremetal.cc b/pw_sys_io_baremetal_stm32f429/sys_io_baremetal.cc
index 80cb169..846e34c 100644
--- a/pw_sys_io_baremetal_stm32f429/sys_io_baremetal.cc
+++ b/pw_sys_io_baremetal_stm32f429/sys_io_baremetal.cc
@@ -170,11 +170,20 @@
 // see if a byte is ready yet.
 Status ReadByte(std::byte* dest) {
   while (true) {
-    if (usart1.status & kReadDataReady) {
-      *dest = static_cast<std::byte>(usart1.data_register);
-      break;
+    if (TryReadByte(dest).ok()) {
+      return Status::Ok();
     }
   }
+}
+
+// Wait for a byte to read on USART1. This blocks until a byte is read. This is
+// extremely inefficient as it requires the target to burn CPU cycles polling to
+// see if a byte is ready yet.
+Status TryReadByte(std::byte* dest) {
+  if (!(usart1.status & kReadDataReady)) {
+    return Status::Unavailable();
+  }
+  *dest = static_cast<std::byte>(usart1.data_register);
   return Status::Ok();
 }
 
diff --git a/pw_sys_io_stdio/sys_io.cc b/pw_sys_io_stdio/sys_io.cc
index 04ab9b0..53c9d5e 100644
--- a/pw_sys_io_stdio/sys_io.cc
+++ b/pw_sys_io_stdio/sys_io.cc
@@ -31,6 +31,11 @@
   return Status::Ok();
 }
 
+Status TryReadByte(std::byte*) {
+  // TryReadByte() is not (yet) supported on hosts.
+  return Status::Unimplemented();
+}
+
 Status WriteByte(std::byte b) {
   if (std::putchar(static_cast<char>(b)) == EOF) {
     return Status::Internal();