summaryrefslogtreecommitdiff
path: root/quantum/os_detection.c
diff options
context:
space:
mode:
authorAndre Brait <andrebrait@gmail.com>2024-02-16 15:19:02 +0100
committerGitHub <noreply@github.com>2024-02-17 01:19:02 +1100
commit80f3da36e571fa702b1d3df693fd545801250eca (patch)
tree8fe09c9e53bbe8534372a87bab3d4edfd61e5fa8 /quantum/os_detection.c
parent77e88674986ee14bd1799b1ab19b4c94af083bac (diff)
[Core] Add OS detection callbacks (#21777)
Diffstat (limited to 'quantum/os_detection.c')
-rw-r--r--quantum/os_detection.c170
1 files changed, 120 insertions, 50 deletions
diff --git a/quantum/os_detection.c b/quantum/os_detection.c
index e606227136..b674f05b35 100644
--- a/quantum/os_detection.c
+++ b/quantum/os_detection.c
@@ -16,20 +16,25 @@
16 16
17#include "os_detection.h" 17#include "os_detection.h"
18 18
19#include <string.h> 19#ifdef OS_DETECTION_ENABLE
20 20
21#ifdef OS_DETECTION_DEBUG_ENABLE 21# include <string.h>
22# include "eeconfig.h" 22# include "timer.h"
23# include "eeprom.h" 23# ifdef OS_DETECTION_KEYBOARD_RESET
24# include "print.h" 24# include "quantum.h"
25# endif
25 26
26# define STORED_USB_SETUPS 50 27# ifdef OS_DETECTION_DEBUG_ENABLE
27# define EEPROM_USER_OFFSET (uint8_t*)EECONFIG_SIZE 28# include "eeconfig.h"
29# include "eeprom.h"
30# include "print.h"
28 31
29uint16_t usb_setups[STORED_USB_SETUPS]; 32# define STORED_USB_SETUPS 50
30#endif 33# define EEPROM_USER_OFFSET (uint8_t*)EECONFIG_SIZE
34
35static uint16_t usb_setups[STORED_USB_SETUPS];
36# endif
31 37
32#ifdef OS_DETECTION_ENABLE
33struct setups_data_t { 38struct setups_data_t {
34 uint8_t count; 39 uint8_t count;
35 uint8_t cnt_02; 40 uint8_t cnt_02;
@@ -45,43 +50,63 @@ struct setups_data_t setups_data = {
45 .cnt_ff = 0, 50 .cnt_ff = 0,
46}; 51};
47 52
48os_variant_t detected_os = OS_UNSURE; 53# ifndef OS_DETECTION_DEBOUNCE
54# define OS_DETECTION_DEBOUNCE 200
55# endif
49 56
50// Some collected sequences of wLength can be found in tests. 57// 2s should always be more than enough (otherwise, you may have other issues)
51void make_guess(void) { 58# if OS_DETECTION_DEBOUNCE > 2000
52 if (setups_data.count < 3) { 59# undef OS_DETECTION_DEBOUNCE
53 return; 60# define OS_DETECTION_DEBOUNCE 2000
54 } 61# endif
55 if (setups_data.cnt_ff >= 2 && setups_data.cnt_04 >= 1) { 62
56 detected_os = OS_WINDOWS; 63typedef uint16_t debouncing_t;
57 return; 64
58 } 65static volatile os_variant_t detected_os = OS_UNSURE;
59 if (setups_data.count == setups_data.cnt_ff) { 66static os_variant_t reported_os = OS_UNSURE;
60 // Linux has 3 packets with 0xFF. 67
61 detected_os = OS_LINUX; 68// we need to be able to report OS_UNSURE if that is the stable result of the guesses
62 return; 69static bool first_report = true;
63 } 70
64 if (setups_data.count == 5 && setups_data.last_wlength == 0xFF && setups_data.cnt_ff == 1 && setups_data.cnt_02 == 2) { 71// to react on USB state changes
65 detected_os = OS_MACOS; 72static volatile enum usb_device_state current_usb_device_state = USB_DEVICE_STATE_INIT;
66 return; 73static enum usb_device_state reported_usb_device_state = USB_DEVICE_STATE_INIT;
67 } 74
68 if (setups_data.count == 4 && setups_data.cnt_ff == 0 && setups_data.cnt_02 == 2) { 75// the OS detection might be unstable for a while, "debounce" it
69 // iOS and iPadOS don't have the last 0xFF packet. 76static volatile bool debouncing = false;
70 detected_os = OS_IOS; 77static volatile fast_timer_t last_time;
71 return; 78
72 } 79void os_detection_task(void) {
73 if (setups_data.cnt_ff == 0 && setups_data.cnt_02 == 3 && setups_data.cnt_04 == 1) { 80 if (current_usb_device_state == USB_DEVICE_STATE_CONFIGURED) {
74 // This is actually PS5. 81 // debouncing goes for both the detected OS as well as the USB state
75 detected_os = OS_LINUX; 82 if (debouncing && timer_elapsed_fast(last_time) >= OS_DETECTION_DEBOUNCE) {
76 return; 83 debouncing = false;
84 reported_usb_device_state = current_usb_device_state;
85 if (detected_os != reported_os || first_report) {
86 first_report = false;
87 reported_os = detected_os;
88 process_detected_host_os_kb(detected_os);
89 }
90 }
77 } 91 }
78 if (setups_data.cnt_ff >= 1 && setups_data.cnt_02 == 0 && setups_data.cnt_04 == 0) { 92# ifdef OS_DETECTION_KEYBOARD_RESET
79 // This is actually Quest 2 or Nintendo Switch. 93 // resetting the keyboard on the USB device state change callback results in instability, so delegate that to this task
80 detected_os = OS_LINUX; 94 // only take action if it's been stable at least once, to avoid issues with some KVMs
81 return; 95 else if (current_usb_device_state == USB_DEVICE_STATE_INIT && reported_usb_device_state != USB_DEVICE_STATE_INIT) {
96 soft_reset_keyboard();
82 } 97 }
98# endif
83} 99}
84 100
101__attribute__((weak)) bool process_detected_host_os_kb(os_variant_t detected_os) {
102 return process_detected_host_os_user(detected_os);
103}
104
105__attribute__((weak)) bool process_detected_host_os_user(os_variant_t detected_os) {
106 return true;
107}
108
109// Some collected sequences of wLength can be found in tests.
85void process_wlength(const uint16_t w_length) { 110void process_wlength(const uint16_t w_length) {
86# ifdef OS_DETECTION_DEBUG_ENABLE 111# ifdef OS_DETECTION_DEBUG_ENABLE
87 usb_setups[setups_data.count] = w_length; 112 usb_setups[setups_data.count] = w_length;
@@ -95,7 +120,37 @@ void process_wlength(const uint16_t w_length) {
95 } else if (w_length == 0xFF) { 120 } else if (w_length == 0xFF) {
96 setups_data.cnt_ff++; 121 setups_data.cnt_ff++;
97 } 122 }
98 make_guess(); 123
124 // now try to make a guess
125 os_variant_t guessed = OS_UNSURE;
126 if (setups_data.count >= 3) {
127 if (setups_data.cnt_ff >= 2 && setups_data.cnt_04 >= 1) {
128 guessed = OS_WINDOWS;
129 } else if (setups_data.count == setups_data.cnt_ff) {
130 // Linux has 3 packets with 0xFF.
131 guessed = OS_LINUX;
132 } else if (setups_data.count == 5 && setups_data.last_wlength == 0xFF && setups_data.cnt_ff == 1 && setups_data.cnt_02 == 2) {
133 guessed = OS_MACOS;
134 } else if (setups_data.count == 4 && setups_data.cnt_ff == 0 && setups_data.cnt_02 == 2) {
135 // iOS and iPadOS don't have the last 0xFF packet.
136 guessed = OS_IOS;
137 } else if (setups_data.cnt_ff == 0 && setups_data.cnt_02 == 3 && setups_data.cnt_04 == 1) {
138 // This is actually PS5.
139 guessed = OS_LINUX;
140 } else if (setups_data.cnt_ff >= 1 && setups_data.cnt_02 == 0 && setups_data.cnt_04 == 0) {
141 // This is actually Quest 2 or Nintendo Switch.
142 guessed = OS_LINUX;
143 }
144 }
145
146 // only replace the guessed value if not unsure
147 if (guessed != OS_UNSURE) {
148 detected_os = guessed;
149 }
150
151 // whatever the result, debounce
152 last_time = timer_read_fast();
153 debouncing = true;
99} 154}
100 155
101os_variant_t detected_host_os(void) { 156os_variant_t detected_host_os(void) {
@@ -104,25 +159,38 @@ os_variant_t detected_host_os(void) {
104 159
105void erase_wlength_data(void) { 160void erase_wlength_data(void) {
106 memset(&setups_data, 0, sizeof(setups_data)); 161 memset(&setups_data, 0, sizeof(setups_data));
107 detected_os = OS_UNSURE; 162 detected_os = OS_UNSURE;
163 reported_os = OS_UNSURE;
164 current_usb_device_state = USB_DEVICE_STATE_INIT;
165 reported_usb_device_state = USB_DEVICE_STATE_INIT;
166 debouncing = false;
167 first_report = true;
168}
169
170void os_detection_notify_usb_device_state_change(enum usb_device_state usb_device_state) {
171 // treat this like any other source of instability
172 current_usb_device_state = usb_device_state;
173 last_time = timer_read_fast();
174 debouncing = true;
108} 175}
109 176
110# if defined(SPLIT_KEYBOARD) && defined(SPLIT_DETECTED_OS_ENABLE) 177# if defined(SPLIT_KEYBOARD) && defined(SPLIT_DETECTED_OS_ENABLE)
111void slave_update_detected_host_os(os_variant_t os) { 178void slave_update_detected_host_os(os_variant_t os) {
112 detected_os = os; 179 detected_os = os;
180 last_time = timer_read_fast();
181 debouncing = true;
113} 182}
114# endif // defined(SPLIT_KEYBOARD) && defined(SPLIT_DETECTED_OS_ENABLE) 183# endif
115#endif // OS_DETECTION_ENABLE
116 184
117#ifdef OS_DETECTION_DEBUG_ENABLE 185# ifdef OS_DETECTION_DEBUG_ENABLE
118void print_stored_setups(void) { 186void print_stored_setups(void) {
119# ifdef CONSOLE_ENABLE 187# ifdef CONSOLE_ENABLE
120 uint8_t cnt = eeprom_read_byte(EEPROM_USER_OFFSET); 188 uint8_t cnt = eeprom_read_byte(EEPROM_USER_OFFSET);
121 for (uint16_t i = 0; i < cnt; ++i) { 189 for (uint16_t i = 0; i < cnt; ++i) {
122 uint16_t* addr = (uint16_t*)EEPROM_USER_OFFSET + i * sizeof(uint16_t) + sizeof(uint8_t); 190 uint16_t* addr = (uint16_t*)EEPROM_USER_OFFSET + i * sizeof(uint16_t) + sizeof(uint8_t);
123 xprintf("i: %d, wLength: 0x%02X\n", i, eeprom_read_word(addr)); 191 xprintf("i: %d, wLength: 0x%02X\n", i, eeprom_read_word(addr));
124 } 192 }
125# endif 193# endif
126} 194}
127 195
128void store_setups_in_eeprom(void) { 196void store_setups_in_eeprom(void) {
@@ -133,4 +201,6 @@ void store_setups_in_eeprom(void) {
133 } 201 }
134} 202}
135 203
136#endif // OS_DETECTION_DEBUG_ENABLE 204# endif // OS_DETECTION_DEBUG_ENABLE
205
206#endif