Skip to content
This repository was archived by the owner on Jun 4, 2026. It is now read-only.

Commit a7cc669

Browse files
committed
usb-mouse: work around (another) Linux kernel bug for high-resolution scrolling
1 parent 63ce831 commit a7cc669

2 files changed

Lines changed: 36 additions & 3 deletions

File tree

usb-mouse/prj.conf

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
CONFIG_LOG=y
2-
#CONFIG_UDC_DRIVER_LOG_LEVEL_INF=y
3-
#CONFIG_C2USB_UDC_MAC_LOG_LEVEL_DBG=y
2+
CONFIG_UDC_DRIVER_LOG_LEVEL_WRN=y
3+
CONFIG_C2USB_UDC_MAC_LOG_LEVEL_DBG=y
44

55
CONFIG_INPUT=y
66
CONFIG_INPUT_MODE_SYNCHRONOUS=y

usb-mouse/src/main.cpp

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,16 +68,49 @@ int main(void)
6868
[](usb::df::device& dev, usb::df::device::event ev)
6969
{
7070
using event = enum usb::df::device::event;
71+
using namespace std::literals;
72+
static high_resolution_mouse<>::resolution_multiplier_report report_backup{};
73+
static os::zephyr::tick_timer::time_point last_reset_time{};
74+
75+
/* Linux hosts produce erroneous behavior when waking up (on a subset of USB ports):
76+
* 1. L2 -> L0
77+
* 2. USB reset
78+
* 3. L0 -> L2
79+
* 4. L2 -> L0
80+
* 5. USB re-enumeration, this time without negotiating high-resolution scrolling
81+
*/
7182
if (ev == event::CONFIGURATION_CHANGE)
7283
{
73-
LOG_INF("USB configured: %u, granted current: %uuA", dev.configured(),
84+
LOG_INF("USB configured: %u, granted current: %uuA", (unsigned)dev.configured(),
7485
dev.granted_bus_current_uA());
86+
if (!dev.configured())
87+
{
88+
last_reset_time = os::zephyr::tick_timer::now();
89+
}
7590
}
7691
else
7792
{
7893
LOG_INF("USB power state: %s, granted current: %uuA",
7994
magic_enum::enum_name(dev.power_state()).data(),
8095
dev.granted_bus_current_uA());
96+
if (dev.power_state() == usb::power::state::L2_SUSPEND)
97+
{
98+
if (dev.configured())
99+
{
100+
report_backup = mouse().multiplier_report();
101+
}
102+
else if (std::chrono::duration_cast<std::chrono::milliseconds>(
103+
os::zephyr::tick_timer::now() - last_reset_time) < 20ms)
104+
{
105+
// reset happened recently, restore the last known multiplier
106+
mouse().set_report(
107+
high_resolution_mouse<>::resolution_multiplier_report::type(),
108+
std::span<const uint8_t>(
109+
const_cast<const uint8_t*>(report_backup.data()),
110+
sizeof(report_backup)));
111+
LOG_INF("restored multiplier: %x", report_backup.resolutions);
112+
}
113+
}
81114
}
82115
});
83116

0 commit comments

Comments
 (0)