From 81b350c8acfe460a1cc4c08e84355a3bc13618c0 Mon Sep 17 00:00:00 2001
From: Kim Streich <kim@infusedinsight.com>
Date: Wed, 23 Mar 2022 15:17:18 +0400
Subject: [PATCH] Added ext_power_usb_only feature

---
 app/CMakeLists.txt           |  1 +
 app/Kconfig                  |  4 +++
 app/src/ext_power_usb_only.c | 67 ++++++++++++++++++++++++++++++++++++
 3 files changed, 72 insertions(+)
 create mode 100644 app/src/ext_power_usb_only.c

diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt
index 1492be16c83..ccf0e56252c 100644
--- a/app/CMakeLists.txt
+++ b/app/CMakeLists.txt
@@ -82,6 +82,7 @@ target_sources_ifdef(CONFIG_ZMK_USB app PRIVATE src/usb_hid.c)
 target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/hog.c)
 target_sources_ifdef(CONFIG_ZMK_RGB_UNDERGLOW app PRIVATE src/rgb_underglow.c)
 target_sources_ifdef(CONFIG_ZMK_BACKLIGHT app PRIVATE src/backlight.c)
+target_sources_ifdef(CONFIG_ZMK_EXT_POWER_USB_ONLY app PRIVATE src/ext_power_usb_only.c)
 target_sources(app PRIVATE src/endpoints.c)
 target_sources(app PRIVATE src/hid_listener.c)
 target_sources(app PRIVATE src/main.c)
diff --git a/app/Kconfig b/app/Kconfig
index fea2203d612..657280087b7 100644
--- a/app/Kconfig
+++ b/app/Kconfig
@@ -402,6 +402,10 @@ config ZMK_EXT_POWER
 	bool "Enable support to control external power output"
 	default y
 
+config ZMK_EXT_POWER_USB_ONLY
+	bool "Turn off external power when USB is disconnected"
+	default n
+
 #Power Management
 endmenu
 
diff --git a/app/src/ext_power_usb_only.c b/app/src/ext_power_usb_only.c
new file mode 100644
index 00000000000..36b3d5e22b3
--- /dev/null
+++ b/app/src/ext_power_usb_only.c
@@ -0,0 +1,67 @@
+#include <settings/settings.h>
+#include <drivers/ext_power.h>
+#include <zmk/activity.h>
+#include <zmk/usb.h>
+#include <zmk/event_manager.h>
+#include <zmk/events/usb_conn_state_changed.h>
+#include <logging/log.h>
+
+LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
+
+#if IS_ENABLED(CONFIG_ZMK_EXT_POWER_USB_ONLY)
+
+static bool initialized = false;
+
+void zmk_ext_power_usb_only_toggle() {
+
+    // Do not touch external power until properly initialized.
+    // This prevents cutting external power until displays and other
+    // components are properly initialized
+    if (initialized == false) {
+        LOG_DBG("zmk_ext_power_usb_only not initialized yet. Ignoring.");
+        return;
+    }
+
+    const struct device *ext_power = device_get_binding("EXT_POWER");
+    const int ext_power_enabled = ext_power_get(ext_power);
+
+    if (zmk_usb_is_powered() == true && ext_power_enabled == false) {
+        LOG_DBG("USB power was connected. Enabling external power.");
+        ext_power_enable(ext_power);
+    } else if (zmk_usb_is_powered() == false && ext_power_enabled == true) {
+        LOG_DBG("USB power was removed. Disabling external power.");
+        ext_power_disable(ext_power);
+    }
+}
+
+static int zmk_ext_power_usb_only_event_listener(const zmk_event_t *eh) {
+    if (as_zmk_usb_conn_state_changed(eh)) {
+        LOG_DBG("USB conn state changed: %d", zmk_usb_is_powered());
+
+        zmk_ext_power_usb_only_toggle();
+    }
+
+    return 0;
+}
+
+ZMK_LISTENER(ext_power, zmk_ext_power_usb_only_event_listener);
+ZMK_SUBSCRIPTION(ext_power, zmk_usb_conn_state_changed);
+
+static int zmk_ext_power_usb_only_init(const struct device *_arg) {
+
+    LOG_DBG("Running zmk_ext_power_usb_only_init");
+
+    initialized = true;
+    zmk_ext_power_usb_only_toggle();
+
+    return 0;
+}
+
+// Initialized ext power usb only after everything else has been
+// initialized.
+//
+// This avoids interrupting the iniitalization of displays for example.
+#define ZMK_EXT_POWER_USB_ONLY_INIT_PRIORITY CONFIG_APPLICATION_INIT_PRIORITY+100
+SYS_INIT(zmk_ext_power_usb_only_init, APPLICATION, ZMK_EXT_POWER_USB_ONLY_INIT_PRIORITY);
+
+#endif