pw_sys_io_zephyr: Make Kconfig optional for bazel usage

Adds a module config option which can be optionally provided
instead of relying on Kconfig machinery. This let's
pw_sys_io_zephyr be used without using Pigweed as a zephyr module.

Change-Id: I587cae44fd80ec27a3fc3468d419d7b32a494177
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/434559
diff --git a/pw_sys_io_zephyr/BUILD.bazel b/pw_sys_io_zephyr/BUILD.bazel
index 59acb88..cbe7c9e 100644
--- a/pw_sys_io_zephyr/BUILD.bazel
+++ b/pw_sys_io_zephyr/BUILD.bazel
@@ -19,9 +19,22 @@
 package(default_visibility = ["//visibility:public"])
 
 zephyr_cc_library(
+    name = "config",
+    hdrs = ["public/pw_sys_io_zephyr/config.h"],
+    strip_include_prefix = "public",
+    deps = [":config_override"],
+)
+
+label_flag(
+    name = "config_override",
+    build_setting_default = "//pw_build:default_module_config",
+)
+
+zephyr_cc_library(
     name = "pw_sys_io_zephyr",
     srcs = ["//pw_sys_io_zephyr:sys_io.cc"],
     deps = [
+        ":config",
         "//pw_sys_io:default_putget_bytes",
         "//pw_sys_io:pw_sys_io.facade",
         "@zephyr//modules/cmsis_6",
@@ -33,6 +46,7 @@
     name = "backend",
     srcs = ["sys_io.cc"],
     deps = [
+        ":config",
         "//pw_sys_io:default_putget_bytes",
         "//pw_sys_io:pw_sys_io.facade",
         "@zephyr_version//:version",
diff --git a/pw_sys_io_zephyr/CMakeLists.txt b/pw_sys_io_zephyr/CMakeLists.txt
index 059b8d2..f53c422 100644
--- a/pw_sys_io_zephyr/CMakeLists.txt
+++ b/pw_sys_io_zephyr/CMakeLists.txt
@@ -18,12 +18,24 @@
   return()
 endif()
 
+pw_add_module_config(pw_sys_io_zephyr_CONFIG)
+
+pw_add_library(pw_sys_io_zephyr.config INTERFACE
+  HEADERS
+    public/pw_sys_io_zephyr/config.h
+  PUBLIC_INCLUDES
+    public
+  PUBLIC_DEPS
+    ${pw_sys_io_zephyr_CONFIG}
+)
+
 pw_add_library(pw_sys_io_zephyr STATIC
   SOURCES
     sys_io.cc
   PUBLIC_DEPS
     pw_sys_io.facade
     pw_sys_io.default_putget_bytes
+    pw_sys_io_zephyr.config
     zephyr_interface
 )
 
diff --git a/pw_sys_io_zephyr/public/pw_sys_io_zephyr/config.h b/pw_sys_io_zephyr/public/pw_sys_io_zephyr/config.h
new file mode 100644
index 0000000..20d942a
--- /dev/null
+++ b/pw_sys_io_zephyr/public/pw_sys_io_zephyr/config.h
@@ -0,0 +1,59 @@
+// Copyright 2026 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#pragma once
+
+#include <type_traits>
+
+#if defined(CONFIG_PIGWEED_SYS_IO)
+
+// When Kconfig is in use, all configuration MUST come through Kconfig.
+#if defined(PW_SYS_IO_ZEPHYR_CONFIG_HEADER) ||        \
+    defined(PW_SYS_IO_ZEPHYR_CONFIG_INIT_PRIORITY) || \
+    defined(PW_SYS_IO_ZEPHYR_CONFIG_USB)
+#error \
+    "Do not define PW_SYS_IO_ZEPHYR_CONFIG options or PW_SYS_IO_ZEPHYR_CONFIG_HEADER when CONFIG_PIGWEED_SYS_IO is enabled in Kconfig. Configure via Kconfig instead."
+#endif  // defined(PW_SYS_IO_ZEPHYR_CONFIG_...)
+
+#include <zephyr/sys/util.h>
+
+#define PW_SYS_IO_ZEPHYR_CONFIG_INIT_PRIORITY \
+  CONFIG_PIGWEED_SYS_IO_INIT_PRIORITY
+#define PW_SYS_IO_ZEPHYR_CONFIG_USB IS_ENABLED(CONFIG_PIGWEED_SYS_IO_USB)
+
+#else  // !defined(CONFIG_PIGWEED_SYS_IO)
+
+// When Kconfig is not in use (e.g., standalone GN/Bazel builds), allow
+// PW_SYS_IO_ZEPHYR_CONFIG_HEADER to provide overrides, falling back to C++
+// defaults.
+#if defined(PW_SYS_IO_ZEPHYR_CONFIG_HEADER)
+#include PW_SYS_IO_ZEPHYR_CONFIG_HEADER
+#endif  // defined(PW_SYS_IO_ZEPHYR_CONFIG_HEADER)
+
+#ifndef PW_SYS_IO_ZEPHYR_CONFIG_INIT_PRIORITY
+#define PW_SYS_IO_ZEPHYR_CONFIG_INIT_PRIORITY 1
+#endif  // PW_SYS_IO_ZEPHYR_CONFIG_INIT_PRIORITY
+static_assert(
+    std::is_integral_v<decltype(PW_SYS_IO_ZEPHYR_CONFIG_INIT_PRIORITY)>);
+
+#ifndef PW_SYS_IO_ZEPHYR_CONFIG_USB
+#define PW_SYS_IO_ZEPHYR_CONFIG_USB 0
+#endif  // PW_SYS_IO_ZEPHYR_CONFIG_USB
+
+#endif  // defined(CONFIG_PIGWEED_SYS_IO)
+
+namespace pw::sys_io::zephyr::config {
+
+inline constexpr bool kUseUsb = PW_SYS_IO_ZEPHYR_CONFIG_USB;
+
+}  // namespace pw::sys_io::zephyr::config
diff --git a/pw_sys_io_zephyr/sys_io.cc b/pw_sys_io_zephyr/sys_io.cc
index 6f2f0ca..5ebaa19 100644
--- a/pw_sys_io_zephyr/sys_io.cc
+++ b/pw_sys_io_zephyr/sys_io.cc
@@ -19,10 +19,12 @@
 #include <zephyr/kernel.h>
 #include <zephyr/usb/usb_device.h>
 
+#include "pw_sys_io_zephyr/config.h"
+
 static int sys_io_init(void) {
   int err = 0;
 
-  if (IS_ENABLED(CONFIG_PIGWEED_SYS_IO_USB)) {
+  if (pw::sys_io::zephyr::config::kUseUsb) {
     err = usb_enable(nullptr);
     if (err) {
       return err;
@@ -34,7 +36,7 @@
   return err;
 }
 
-SYS_INIT(sys_io_init, APPLICATION, CONFIG_PIGWEED_SYS_IO_INIT_PRIORITY);
+SYS_INIT(sys_io_init, APPLICATION, PW_SYS_IO_ZEPHYR_CONFIG_INIT_PRIORITY);
 
 namespace pw::sys_io {