keyboard stuff
1/* Copyright 2022 Ruslan Sayfutdinov (@KapJI)
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "gtest/gtest.h"
18
19extern "C" {
20#include "os_detection.h"
21#include "timer.h"
22
23void advance_time(uint32_t ms);
24}
25
26static uint32_t reported_count;
27static os_variant_t reported_os;
28
29class OsDetectionTest : public ::testing::Test {
30 protected:
31 void SetUp() override {
32 erase_wlength_data();
33 reported_count = 0;
34 reported_os = OS_UNSURE;
35 }
36};
37
38os_variant_t check_sequence(const std::vector<uint16_t> &w_lengths) {
39 for (auto &w_length : w_lengths) {
40 process_wlength(w_length);
41 }
42 return detected_host_os();
43}
44
45bool process_detected_host_os_kb(os_variant_t os) {
46 reported_count = reported_count + 1;
47 reported_os = os;
48
49 return true;
50}
51
52void assert_not_reported(void) {
53 // check that it does not report the result, nor any intermediate results
54 EXPECT_EQ(reported_count, 0);
55 EXPECT_EQ(reported_os, OS_UNSURE);
56}
57
58void assert_reported(os_variant_t os) {
59 // check that it reports exclusively the result, not any intermediate results
60 EXPECT_EQ(reported_count, 1);
61 EXPECT_EQ(reported_os, os);
62 EXPECT_EQ(reported_os, detected_host_os());
63}
64
65/* Some collected data.
66
67ChibiOS:
68Windows 10: [FF, FF, 4, 24, 4, 24, 4, FF, 24, FF, 4, FF, 24, 4, 24, 20A, 20A, 20A, 20A, 20A, 20A, 20A, 20A, 20A, 20A, 20A, 20A, 20A, 20A, 20A, 20A, 20A, 20A, 20A, 20A, 20A, 20A, 20A, 20A]
69Windows 10 (another host): [FF, FF, 4, 24, 4, 24, 4, 24, 4, 24, 4, 24]
70macOS 12.5: [2, 24, 2, 28, FF]
71macOS 15.1.x: [ 2, 4E, 2, 1C, 2, 1A, FF, FF]
72macOS 15.x (another host): [ 2, 0E, 2, 1E, 2, 42, FF]
73macOS 15.x (periodic weirdness): [ 2, 42, 2, 1C, 2, 1A, FF, 2, 42, 2, 1C, 2, 1A, FF ]
74iOS/iPadOS 15.6: [2, 24, 2, 28]
75Linux (including Android, Raspberry Pi and WebOS TV): [FF, FF, FF]
76Linux (another host): [FF, FF, FF, FF, FF, FF]
77PS5: [2, 4, 2, 28, 2, 24]
78Nintendo Switch: [82, FF, 40, 40, FF, 40, 40, FF, 40, 40, FF, 40, 40, FF, 40, 40]
79Quest 2: [FF, FF, FF, FE, FF, FE, FF, FE, FF, FE, FF]
80
81LUFA:
82Windows 10 (first connect): [12, FF, FF, 4, 10, FF, FF, FF, 4, 10, 20A, 20A, 20A, 20A, 20A, 20A]
83Windows 10 (subsequent connect): [FF, FF, 4, 10, FF, 4, FF, 10, FF, 20A, 20A, 20A, 20A, 20A, 20A]
84Windows 10 (another host): [FF, FF, 4, 10, 4, 10]
85macOS: [2, 10, 2, E, FF]
86macOS 15.x: [ 2, 64, 2, 28, FF, FF]
87iOS/iPadOS: [2, 10, 2, E]
88Linux: [FF, FF, FF]
89PS5: [2, 4, 2, E, 2, 10]
90Nintendo Switch: [82, FF, 40, 40, FF, 40, 40]
91
92V-USB:
93Windows 10: [FF, FF, 4, E, FF]
94Windows 10 (another host): [FF, FF, 4, E, 4]
95macOS: [2, E, 2, E, FF]
96iOS/iPadOS: [2, E, 2, E]
97Linux: [FF, FF, FF]
98PS5: [2, 4, 2, E, 2]
99Nintendo Switch: [82, FF, 40, 40]
100Quest 2: [FF, FF, FF, FE]
101
102Common parts:
103Windows: [..., FF, FF, 4, ...]
104macOS: [2, _, 2, _, FF]
105iOS/iPadOS: [2, _, 2, _]
106Linux: [FF, FF, FF]
107PS5: [2, 4, 2, _, 2, ...]
108Nintendo Switch: [82, FF, 40, 40, ...]
109Quest 2: [FF, FF, FF, FE, ...]
110*/
111TEST_F(OsDetectionTest, TestLinux) {
112 EXPECT_EQ(check_sequence({0xFF, 0xFF, 0xFF}), OS_LINUX);
113 os_detection_task();
114 assert_not_reported();
115}
116
117TEST_F(OsDetectionTest, TestChibiosLinux) {
118 EXPECT_EQ(check_sequence({0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}), OS_LINUX);
119 os_detection_task();
120 assert_not_reported();
121}
122
123TEST_F(OsDetectionTest, TestChibiosMacos) {
124 EXPECT_EQ(check_sequence({0x2, 0x24, 0x2, 0x28, 0xFF}), OS_MACOS);
125 os_detection_task();
126 assert_not_reported();
127}
128
129TEST_F(OsDetectionTest, TestChibiosMacos2) {
130 EXPECT_EQ(check_sequence({0x2, 0x42, 0x2, 0x1C, 0x2, 0x1A, 0xFF}), OS_MACOS);
131 os_detection_task();
132 assert_not_reported();
133}
134
135TEST_F(OsDetectionTest, TestChibiosMacos3) {
136 EXPECT_EQ(check_sequence({0x2, 0x42, 0x2, 0x1C, 0x2, 0x1A, 0xFF, 0x2, 0x42, 0x2, 0x1C, 0x2, 0x1A, 0xFF}), OS_MACOS);
137 os_detection_task();
138 assert_not_reported();
139}
140
141// Regression reported in https://github.com/qmk/qmk_firmware/pull/21777#issuecomment-1922815841
142TEST_F(OsDetectionTest, TestChibiosMacM1) {
143 EXPECT_EQ(check_sequence({0x02, 0x32, 0x02, 0x24, 0x101, 0xFF}), OS_MACOS);
144 os_detection_task();
145 assert_not_reported();
146}
147
148TEST_F(OsDetectionTest, TestChibiosMacSequoia) {
149 EXPECT_EQ(check_sequence({0x02, 0x4E, 0x02, 0x1C, 0x02, 0x1A, 0xFF, 0xFF}), OS_MACOS);
150 os_detection_task();
151 assert_not_reported();
152}
153
154TEST_F(OsDetectionTest, TestChibiosMacSequoia2) {
155 EXPECT_EQ(check_sequence({0x02, 0x4E, 0x02, 0x1C, 0x02, 0x1A, 0xFF, 0x02, 0x42, 0x02, 0x1C, 0x02, 0x1A, 0xFF}), OS_MACOS);
156 os_detection_task();
157 assert_not_reported();
158}
159
160TEST_F(OsDetectionTest, TestChibiosMacSequoia3) {
161 EXPECT_EQ(check_sequence({0x02, 0x0E, 0x02, 0x1E, 0x02, 0x42, 0xFF}), OS_MACOS);
162 os_detection_task();
163 assert_not_reported();
164}
165
166TEST_F(OsDetectionTest, TestLufaMacos) {
167 EXPECT_EQ(check_sequence({0x2, 0x10, 0x2, 0xE, 0xFF}), OS_MACOS);
168 os_detection_task();
169 assert_not_reported();
170}
171
172TEST_F(OsDetectionTest, TestDetectLufaMacSequoia2) {
173 EXPECT_EQ(check_sequence({0x02, 0x64, 0x02, 0x28, 0xFF, 0xFF}), OS_MACOS);
174 os_detection_task();
175 assert_not_reported();
176}
177
178TEST_F(OsDetectionTest, TestVusbMacos) {
179 EXPECT_EQ(check_sequence({0x2, 0xE, 0x2, 0xE, 0xFF}), OS_MACOS);
180 os_detection_task();
181 assert_not_reported();
182}
183
184TEST_F(OsDetectionTest, TestChibiosIos) {
185 EXPECT_EQ(check_sequence({0x2, 0x24, 0x2, 0x28}), OS_IOS);
186 os_detection_task();
187 assert_not_reported();
188}
189
190TEST_F(OsDetectionTest, TestLufaIos) {
191 EXPECT_EQ(check_sequence({0x2, 0x10, 0x2, 0xE}), OS_IOS);
192 os_detection_task();
193 assert_not_reported();
194}
195
196TEST_F(OsDetectionTest, TestVusbIos) {
197 EXPECT_EQ(check_sequence({0x2, 0xE, 0x2, 0xE}), OS_IOS);
198 os_detection_task();
199 assert_not_reported();
200}
201
202TEST_F(OsDetectionTest, TestChibiosWindows10) {
203 EXPECT_EQ(check_sequence({0xFF, 0xFF, 0x4, 0x24, 0x4, 0x24, 0x4, 0xFF, 0x24, 0xFF, 0x4, 0xFF, 0x24, 0x4, 0x24, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A}), OS_WINDOWS);
204 os_detection_task();
205 assert_not_reported();
206}
207
208TEST_F(OsDetectionTest, TestChibiosWindows10_2) {
209 EXPECT_EQ(check_sequence({0xFF, 0xFF, 0x4, 0x24, 0x4, 0x24, 0x4, 0x24, 0x4, 0x24, 0x4, 0x24}), OS_WINDOWS);
210 os_detection_task();
211 assert_not_reported();
212}
213
214TEST_F(OsDetectionTest, TestLufaWindows10) {
215 EXPECT_EQ(check_sequence({0x12, 0xFF, 0xFF, 0x4, 0x10, 0xFF, 0xFF, 0xFF, 0x4, 0x10, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A}), OS_WINDOWS);
216 os_detection_task();
217 assert_not_reported();
218}
219
220TEST_F(OsDetectionTest, TestLufaWindows10_2) {
221 EXPECT_EQ(check_sequence({0xFF, 0xFF, 0x4, 0x10, 0xFF, 0x4, 0xFF, 0x10, 0xFF, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A}), OS_WINDOWS);
222 os_detection_task();
223 assert_not_reported();
224}
225
226TEST_F(OsDetectionTest, TestLufaWindows10_3) {
227 EXPECT_EQ(check_sequence({0xFF, 0xFF, 0x4, 0x10, 0x4, 0x10}), OS_WINDOWS);
228 os_detection_task();
229 assert_not_reported();
230}
231
232TEST_F(OsDetectionTest, TestVusbWindows10) {
233 EXPECT_EQ(check_sequence({0xFF, 0xFF, 0x4, 0xE, 0xFF}), OS_WINDOWS);
234 os_detection_task();
235 assert_not_reported();
236}
237
238TEST_F(OsDetectionTest, TestVusbWindows10_2) {
239 EXPECT_EQ(check_sequence({0xFF, 0xFF, 0x4, 0xE, 0x4}), OS_WINDOWS);
240 os_detection_task();
241 assert_not_reported();
242}
243
244TEST_F(OsDetectionTest, TestChibiosPs5) {
245 EXPECT_EQ(check_sequence({0x2, 0x4, 0x2, 0x28, 0x2, 0x24}), OS_LINUX);
246 os_detection_task();
247 assert_not_reported();
248}
249
250TEST_F(OsDetectionTest, TestLufaPs5) {
251 EXPECT_EQ(check_sequence({0x2, 0x4, 0x2, 0xE, 0x2, 0x10}), OS_LINUX);
252 os_detection_task();
253 assert_not_reported();
254}
255
256TEST_F(OsDetectionTest, TestVusbPs5) {
257 EXPECT_EQ(check_sequence({0x2, 0x4, 0x2, 0xE, 0x2}), OS_LINUX);
258 os_detection_task();
259 assert_not_reported();
260}
261
262TEST_F(OsDetectionTest, TestChibiosNintendoSwitch) {
263 EXPECT_EQ(check_sequence({0x82, 0xFF, 0x40, 0x40, 0xFF, 0x40, 0x40, 0xFF, 0x40, 0x40, 0xFF, 0x40, 0x40, 0xFF, 0x40, 0x40}), OS_LINUX);
264 os_detection_task();
265 assert_not_reported();
266}
267
268TEST_F(OsDetectionTest, TestLufaNintendoSwitch) {
269 EXPECT_EQ(check_sequence({0x82, 0xFF, 0x40, 0x40, 0xFF, 0x40, 0x40}), OS_LINUX);
270 os_detection_task();
271 assert_not_reported();
272}
273
274TEST_F(OsDetectionTest, TestVusbNintendoSwitch) {
275 EXPECT_EQ(check_sequence({0x82, 0xFF, 0x40, 0x40}), OS_LINUX);
276 os_detection_task();
277 assert_not_reported();
278}
279
280TEST_F(OsDetectionTest, TestChibiosQuest2) {
281 EXPECT_EQ(check_sequence({0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF}), OS_LINUX);
282 os_detection_task();
283 assert_not_reported();
284}
285
286TEST_F(OsDetectionTest, TestVusbQuest2) {
287 EXPECT_EQ(check_sequence({0xFF, 0xFF, 0xFF, 0xFE}), OS_LINUX);
288 os_detection_task();
289 assert_not_reported();
290}
291
292TEST_F(OsDetectionTest, TestDoNotReportIfUsbUnstable) {
293 EXPECT_EQ(check_sequence({0xFF, 0xFF, 0xFF, 0xFE}), OS_LINUX);
294 os_detection_task();
295 assert_not_reported();
296
297 advance_time(OS_DETECTION_DEBOUNCE);
298 os_detection_task();
299 assert_not_reported();
300 EXPECT_EQ(detected_host_os(), OS_LINUX);
301}
302
303static struct usb_device_state usb_device_state_configured = {.configure_state = USB_DEVICE_STATE_CONFIGURED};
304
305TEST_F(OsDetectionTest, TestReportAfterDebounce) {
306 EXPECT_EQ(check_sequence({0xFF, 0xFF, 0xFF, 0xFE}), OS_LINUX);
307 os_detection_notify_usb_device_state_change(usb_device_state_configured);
308 os_detection_task();
309 assert_not_reported();
310
311 advance_time(1);
312 os_detection_task();
313 assert_not_reported();
314 EXPECT_EQ(detected_host_os(), OS_LINUX);
315
316 advance_time(OS_DETECTION_DEBOUNCE - 3);
317 os_detection_task();
318 assert_not_reported();
319 EXPECT_EQ(detected_host_os(), OS_LINUX);
320
321 advance_time(1);
322 os_detection_task();
323 assert_not_reported();
324 EXPECT_EQ(detected_host_os(), OS_LINUX);
325
326 // advancing the timer alone must not cause a report
327 advance_time(1);
328 assert_not_reported();
329 EXPECT_EQ(detected_host_os(), OS_LINUX);
330 // the task will cause a report
331 os_detection_task();
332 assert_reported(OS_LINUX);
333 EXPECT_EQ(detected_host_os(), OS_LINUX);
334
335 // check that it remains the same after a long time
336 advance_time(OS_DETECTION_DEBOUNCE * 15);
337 assert_reported(OS_LINUX);
338 EXPECT_EQ(detected_host_os(), OS_LINUX);
339}
340
341TEST_F(OsDetectionTest, TestReportAfterDebounceLongWait) {
342 EXPECT_EQ(check_sequence({0x12, 0xFF, 0xFF, 0x4, 0x10, 0xFF, 0xFF, 0xFF, 0x4, 0x10, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A}), OS_WINDOWS);
343 os_detection_notify_usb_device_state_change(usb_device_state_configured);
344 os_detection_task();
345 assert_not_reported();
346
347 advance_time(1);
348 os_detection_task();
349 assert_not_reported();
350 EXPECT_EQ(detected_host_os(), OS_WINDOWS);
351
352 // advancing the timer alone must not cause a report
353 advance_time(OS_DETECTION_DEBOUNCE * 15);
354 assert_not_reported();
355 EXPECT_EQ(detected_host_os(), OS_WINDOWS);
356 // the task will cause a report
357 os_detection_task();
358 assert_reported(OS_WINDOWS);
359 EXPECT_EQ(detected_host_os(), OS_WINDOWS);
360
361 // check that it remains the same after a long time
362 advance_time(OS_DETECTION_DEBOUNCE * 10);
363 os_detection_task();
364 assert_reported(OS_WINDOWS);
365 EXPECT_EQ(detected_host_os(), OS_WINDOWS);
366}
367
368TEST_F(OsDetectionTest, TestReportUnsure) {
369 EXPECT_EQ(check_sequence({0x12, 0xFF}), OS_UNSURE);
370 os_detection_notify_usb_device_state_change(usb_device_state_configured);
371 os_detection_task();
372 assert_not_reported();
373
374 advance_time(1);
375 os_detection_task();
376 assert_not_reported();
377 EXPECT_EQ(detected_host_os(), OS_UNSURE);
378
379 // advancing the timer alone must not cause a report
380 advance_time(OS_DETECTION_DEBOUNCE - 1);
381 assert_not_reported();
382 EXPECT_EQ(detected_host_os(), OS_UNSURE);
383 // the task will cause a report
384 os_detection_task();
385 assert_reported(OS_UNSURE);
386 EXPECT_EQ(detected_host_os(), OS_UNSURE);
387
388 // check that it remains the same after a long time
389 advance_time(OS_DETECTION_DEBOUNCE * 10);
390 os_detection_task();
391 assert_reported(OS_UNSURE);
392 EXPECT_EQ(detected_host_os(), OS_UNSURE);
393}
394
395TEST_F(OsDetectionTest, TestDoNotReportIntermediateResults) {
396 EXPECT_EQ(check_sequence({0x12, 0xFF}), OS_UNSURE);
397 os_detection_notify_usb_device_state_change(usb_device_state_configured);
398 os_detection_task();
399 assert_not_reported();
400
401 advance_time(OS_DETECTION_DEBOUNCE - 1);
402 os_detection_task();
403 assert_not_reported();
404 EXPECT_EQ(detected_host_os(), OS_UNSURE);
405
406 // at this stage, the final result has not been reached yet
407 EXPECT_EQ(check_sequence({0xFF}), OS_LINUX);
408 os_detection_notify_usb_device_state_change(usb_device_state_configured);
409 advance_time(OS_DETECTION_DEBOUNCE - 1);
410 os_detection_task();
411 assert_not_reported();
412 // the intermedite but yet unstable result is exposed through detected_host_os()
413 EXPECT_EQ(detected_host_os(), OS_LINUX);
414
415 // the remainder is processed
416 EXPECT_EQ(check_sequence({0x4, 0x10, 0xFF, 0xFF, 0xFF, 0x4, 0x10, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A}), OS_WINDOWS);
417 os_detection_notify_usb_device_state_change(usb_device_state_configured);
418 advance_time(OS_DETECTION_DEBOUNCE - 1);
419 os_detection_task();
420 assert_not_reported();
421 EXPECT_EQ(detected_host_os(), OS_WINDOWS);
422
423 // advancing the timer alone must not cause a report
424 advance_time(1);
425 assert_not_reported();
426 EXPECT_EQ(detected_host_os(), OS_WINDOWS);
427 // the task will cause a report
428 os_detection_task();
429 assert_reported(OS_WINDOWS);
430 EXPECT_EQ(detected_host_os(), OS_WINDOWS);
431
432 // check that it remains the same after a long time
433 advance_time(OS_DETECTION_DEBOUNCE * 10);
434 os_detection_task();
435 assert_reported(OS_WINDOWS);
436 EXPECT_EQ(detected_host_os(), OS_WINDOWS);
437}
438
439TEST_F(OsDetectionTest, TestDoNotGoBackToUnsure) {
440 // 0x02 would cause it to go back to Unsure, so check that it does not
441 EXPECT_EQ(check_sequence({0xFF, 0xFF, 0xFF, 0xFE, 0x02}), OS_LINUX);
442 os_detection_task();
443 assert_not_reported();
444}