Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions app/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,18 @@ config USB_NUMOF_EP_WRITE_RETRIES
config USB_HID_POLL_INTERVAL_MS
default 1

config ZMK_USB_SUSPEND_DISCONNECT_TIMEOUT_MS
int "USB suspend disconnect timeout in milliseconds"
default 0
help
When non-zero, treat a USB suspend lasting longer than this many
milliseconds as a disconnect. This is useful for boards without
VBUS detection where the USB controller reports USB_DC_SUSPEND
when the cable is physically unplugged, preventing the endpoint
from switching to BLE.

Set to 0 (default) to disable this workaround.

endif # ZMK_USB

menuconfig ZMK_BLE
Expand Down
7 changes: 7 additions & 0 deletions app/boards/lowprokb/corneish_zen/Kconfig.defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ config USB_NRFX
config USB_DEVICE_STACK
default y

# The Zen board has no VBUS detection. When the USB cable is
# unplugged, the nRF52840 reports USB_DC_SUSPEND, which ZMK treats as
# "still connected." A 2-second timeout causes the keyboard to fall
# back to BLE after cable disconnect.
config ZMK_USB_SUSPEND_DISCONNECT_TIMEOUT_MS
default 2000

endif # USB

config ZMK_DISPLAY
Expand Down
36 changes: 36 additions & 0 deletions app/src/usb.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,33 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
static enum usb_dc_status_code usb_status = USB_DC_UNKNOWN;
static bool is_configured;

#define SUSPEND_TIMEOUT_ACTIVE (CONFIG_ZMK_USB_SUSPEND_DISCONNECT_TIMEOUT_MS > 0)

#if SUSPEND_TIMEOUT_ACTIVE

static void usb_suspend_timeout_fn(struct k_timer *timer);
static K_TIMER_DEFINE(usb_suspend_timer, usb_suspend_timeout_fn, NULL);

static void usb_suspend_timeout_fn(struct k_timer *timer) {
// If we are still in SUSPEND after the timeout, the cable was
// likely unplugged (no VBUS detection). Treat as disconnect so
// the endpoint falls back to BLE.
if (usb_status == USB_DC_SUSPEND) {
usb_status = USB_DC_DISCONNECTED;
is_configured = false;
k_work_submit(&usb_status_notifier_work);
}
}

static inline void suspend_timeout_start(void) {
k_timer_start(&usb_suspend_timer, K_MSEC(CONFIG_ZMK_USB_SUSPEND_DISCONNECT_TIMEOUT_MS),
K_NO_WAIT);
}

static inline void suspend_timeout_cancel(void) { k_timer_stop(&usb_suspend_timer); }

#endif

static void raise_usb_status_changed_event(struct k_work *_work) {
raise_zmk_usb_conn_state_changed(
(struct zmk_usb_conn_state_changed){.conn_state = zmk_usb_get_conn_state()});
Expand Down Expand Up @@ -66,6 +93,15 @@ void usb_status_cb(enum usb_dc_status_code status, const uint8_t *params) {
zmk_usb_hid_set_protocol(HID_PROTOCOL_REPORT);
}
#endif

#if SUSPEND_TIMEOUT_ACTIVE
if (status == USB_DC_SUSPEND) {
suspend_timeout_start();
} else {
suspend_timeout_cancel();
}
#endif

usb_status = status;
if (zmk_usb_get_conn_state() == ZMK_USB_CONN_HID) {
is_configured |= usb_status == USB_DC_CONFIGURED;
Expand Down