os_detection.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  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. #include "gtest/gtest.h"
  17. extern "C" {
  18. #include "os_detection.h"
  19. #include "timer.h"
  20. void advance_time(uint32_t ms);
  21. }
  22. static uint32_t reported_count;
  23. static os_variant_t reported_os;
  24. class OsDetectionTest : public ::testing::Test {
  25. protected:
  26. void SetUp() override {
  27. erase_wlength_data();
  28. reported_count = 0;
  29. reported_os = OS_UNSURE;
  30. }
  31. };
  32. os_variant_t check_sequence(const std::vector<uint16_t> &w_lengths) {
  33. for (auto &w_length : w_lengths) {
  34. process_wlength(w_length);
  35. }
  36. return detected_host_os();
  37. }
  38. bool process_detected_host_os_kb(os_variant_t os) {
  39. reported_count = reported_count + 1;
  40. reported_os = os;
  41. return true;
  42. }
  43. void assert_not_reported(void) {
  44. // check that it does not report the result, nor any intermediate results
  45. EXPECT_EQ(reported_count, 0);
  46. EXPECT_EQ(reported_os, OS_UNSURE);
  47. }
  48. void assert_reported(os_variant_t os) {
  49. // check that it reports exclusively the result, not any intermediate results
  50. EXPECT_EQ(reported_count, 1);
  51. EXPECT_EQ(reported_os, os);
  52. EXPECT_EQ(reported_os, detected_host_os());
  53. }
  54. /* Some collected data.
  55. ChibiOS:
  56. Windows 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]
  57. Windows 10 (another host): [FF, FF, 4, 24, 4, 24, 4, 24, 4, 24, 4, 24]
  58. macOS 12.5: [2, 24, 2, 28, FF]
  59. iOS/iPadOS 15.6: [2, 24, 2, 28]
  60. Linux (including Android, Raspberry Pi and WebOS TV): [FF, FF, FF]
  61. PS5: [2, 4, 2, 28, 2, 24]
  62. Nintendo Switch: [82, FF, 40, 40, FF, 40, 40, FF, 40, 40, FF, 40, 40, FF, 40, 40]
  63. Quest 2: [FF, FF, FF, FE, FF, FE, FF, FE, FF, FE, FF]
  64. LUFA:
  65. Windows 10 (first connect): [12, FF, FF, 4, 10, FF, FF, FF, 4, 10, 20A, 20A, 20A, 20A, 20A, 20A]
  66. Windows 10 (subsequent connect): [FF, FF, 4, 10, FF, 4, FF, 10, FF, 20A, 20A, 20A, 20A, 20A, 20A]
  67. Windows 10 (another host): [FF, FF, 4, 10, 4, 10]
  68. macOS: [2, 10, 2, E, FF]
  69. iOS/iPadOS: [2, 10, 2, E]
  70. Linux: [FF, FF, FF]
  71. PS5: [2, 4, 2, E, 2, 10]
  72. Nintendo Switch: [82, FF, 40, 40, FF, 40, 40]
  73. V-USB:
  74. Windows 10: [FF, FF, 4, E, FF]
  75. Windows 10 (another host): [FF, FF, 4, E, 4]
  76. macOS: [2, E, 2, E, FF]
  77. iOS/iPadOS: [2, E, 2, E]
  78. Linux: [FF, FF, FF]
  79. PS5: [2, 4, 2, E, 2]
  80. Nintendo Switch: [82, FF, 40, 40]
  81. Quest 2: [FF, FF, FF, FE]
  82. Common parts:
  83. Windows: [..., FF, FF, 4, ...]
  84. macOS: [2, _, 2, _, FF]
  85. iOS/iPadOS: [2, _, 2, _]
  86. Linux: [FF, FF, FF]
  87. PS5: [2, 4, 2, _, 2, ...]
  88. Nintendo Switch: [82, FF, 40, 40, ...]
  89. Quest 2: [FF, FF, FF, FE, ...]
  90. */
  91. TEST_F(OsDetectionTest, TestLinux) {
  92. EXPECT_EQ(check_sequence({0xFF, 0xFF, 0xFF}), OS_LINUX);
  93. os_detection_task();
  94. assert_not_reported();
  95. }
  96. TEST_F(OsDetectionTest, TestChibiosMacos) {
  97. EXPECT_EQ(check_sequence({0x2, 0x24, 0x2, 0x28, 0xFF}), OS_MACOS);
  98. os_detection_task();
  99. assert_not_reported();
  100. }
  101. TEST_F(OsDetectionTest, TestLufaMacos) {
  102. EXPECT_EQ(check_sequence({0x2, 0x10, 0x2, 0xE, 0xFF}), OS_MACOS);
  103. os_detection_task();
  104. assert_not_reported();
  105. }
  106. TEST_F(OsDetectionTest, TestVusbMacos) {
  107. EXPECT_EQ(check_sequence({0x2, 0xE, 0x2, 0xE, 0xFF}), OS_MACOS);
  108. os_detection_task();
  109. assert_not_reported();
  110. }
  111. TEST_F(OsDetectionTest, TestChibiosIos) {
  112. EXPECT_EQ(check_sequence({0x2, 0x24, 0x2, 0x28}), OS_IOS);
  113. os_detection_task();
  114. assert_not_reported();
  115. }
  116. TEST_F(OsDetectionTest, TestLufaIos) {
  117. EXPECT_EQ(check_sequence({0x2, 0x10, 0x2, 0xE}), OS_IOS);
  118. os_detection_task();
  119. assert_not_reported();
  120. }
  121. TEST_F(OsDetectionTest, TestVusbIos) {
  122. EXPECT_EQ(check_sequence({0x2, 0xE, 0x2, 0xE}), OS_IOS);
  123. os_detection_task();
  124. assert_not_reported();
  125. }
  126. TEST_F(OsDetectionTest, TestChibiosWindows10) {
  127. 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);
  128. os_detection_task();
  129. assert_not_reported();
  130. }
  131. TEST_F(OsDetectionTest, TestChibiosWindows10_2) {
  132. EXPECT_EQ(check_sequence({0xFF, 0xFF, 0x4, 0x24, 0x4, 0x24, 0x4, 0x24, 0x4, 0x24, 0x4, 0x24}), OS_WINDOWS);
  133. os_detection_task();
  134. assert_not_reported();
  135. }
  136. TEST_F(OsDetectionTest, TestLufaWindows10) {
  137. EXPECT_EQ(check_sequence({0x12, 0xFF, 0xFF, 0x4, 0x10, 0xFF, 0xFF, 0xFF, 0x4, 0x10, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A}), OS_WINDOWS);
  138. os_detection_task();
  139. assert_not_reported();
  140. }
  141. TEST_F(OsDetectionTest, TestLufaWindows10_2) {
  142. EXPECT_EQ(check_sequence({0xFF, 0xFF, 0x4, 0x10, 0xFF, 0x4, 0xFF, 0x10, 0xFF, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A}), OS_WINDOWS);
  143. os_detection_task();
  144. assert_not_reported();
  145. }
  146. TEST_F(OsDetectionTest, TestLufaWindows10_3) {
  147. EXPECT_EQ(check_sequence({0xFF, 0xFF, 0x4, 0x10, 0x4, 0x10}), OS_WINDOWS);
  148. os_detection_task();
  149. assert_not_reported();
  150. }
  151. TEST_F(OsDetectionTest, TestVusbWindows10) {
  152. EXPECT_EQ(check_sequence({0xFF, 0xFF, 0x4, 0xE, 0xFF}), OS_WINDOWS);
  153. os_detection_task();
  154. assert_not_reported();
  155. }
  156. TEST_F(OsDetectionTest, TestVusbWindows10_2) {
  157. EXPECT_EQ(check_sequence({0xFF, 0xFF, 0x4, 0xE, 0x4}), OS_WINDOWS);
  158. os_detection_task();
  159. assert_not_reported();
  160. }
  161. TEST_F(OsDetectionTest, TestChibiosPs5) {
  162. EXPECT_EQ(check_sequence({0x2, 0x4, 0x2, 0x28, 0x2, 0x24}), OS_LINUX);
  163. os_detection_task();
  164. assert_not_reported();
  165. }
  166. TEST_F(OsDetectionTest, TestLufaPs5) {
  167. EXPECT_EQ(check_sequence({0x2, 0x4, 0x2, 0xE, 0x2, 0x10}), OS_LINUX);
  168. os_detection_task();
  169. assert_not_reported();
  170. }
  171. TEST_F(OsDetectionTest, TestVusbPs5) {
  172. EXPECT_EQ(check_sequence({0x2, 0x4, 0x2, 0xE, 0x2}), OS_LINUX);
  173. os_detection_task();
  174. assert_not_reported();
  175. }
  176. TEST_F(OsDetectionTest, TestChibiosNintendoSwitch) {
  177. EXPECT_EQ(check_sequence({0x82, 0xFF, 0x40, 0x40, 0xFF, 0x40, 0x40, 0xFF, 0x40, 0x40, 0xFF, 0x40, 0x40, 0xFF, 0x40, 0x40}), OS_LINUX);
  178. os_detection_task();
  179. assert_not_reported();
  180. }
  181. TEST_F(OsDetectionTest, TestLufaNintendoSwitch) {
  182. EXPECT_EQ(check_sequence({0x82, 0xFF, 0x40, 0x40, 0xFF, 0x40, 0x40}), OS_LINUX);
  183. os_detection_task();
  184. assert_not_reported();
  185. }
  186. TEST_F(OsDetectionTest, TestVusbNintendoSwitch) {
  187. EXPECT_EQ(check_sequence({0x82, 0xFF, 0x40, 0x40}), OS_LINUX);
  188. os_detection_task();
  189. assert_not_reported();
  190. }
  191. TEST_F(OsDetectionTest, TestChibiosQuest2) {
  192. EXPECT_EQ(check_sequence({0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF}), OS_LINUX);
  193. os_detection_task();
  194. assert_not_reported();
  195. }
  196. TEST_F(OsDetectionTest, TestVusbQuest2) {
  197. EXPECT_EQ(check_sequence({0xFF, 0xFF, 0xFF, 0xFE}), OS_LINUX);
  198. os_detection_task();
  199. assert_not_reported();
  200. }
  201. // Regression reported in https://github.com/qmk/qmk_firmware/pull/21777#issuecomment-1922815841
  202. TEST_F(OsDetectionTest, TestDetectMacM1AsIOS) {
  203. EXPECT_EQ(check_sequence({0x02, 0x32, 0x02, 0x24, 0x101, 0xFF}), OS_IOS);
  204. os_detection_task();
  205. assert_not_reported();
  206. }
  207. TEST_F(OsDetectionTest, TestDoNotReportIfUsbUnstable) {
  208. EXPECT_EQ(check_sequence({0xFF, 0xFF, 0xFF, 0xFE}), OS_LINUX);
  209. os_detection_task();
  210. assert_not_reported();
  211. advance_time(OS_DETECTION_DEBOUNCE);
  212. os_detection_task();
  213. assert_not_reported();
  214. EXPECT_EQ(detected_host_os(), OS_LINUX);
  215. }
  216. static struct usb_device_state usb_device_state_configured = {.configure_state = USB_DEVICE_STATE_CONFIGURED};
  217. TEST_F(OsDetectionTest, TestReportAfterDebounce) {
  218. EXPECT_EQ(check_sequence({0xFF, 0xFF, 0xFF, 0xFE}), OS_LINUX);
  219. os_detection_notify_usb_device_state_change(usb_device_state_configured);
  220. os_detection_task();
  221. assert_not_reported();
  222. advance_time(1);
  223. os_detection_task();
  224. assert_not_reported();
  225. EXPECT_EQ(detected_host_os(), OS_LINUX);
  226. advance_time(OS_DETECTION_DEBOUNCE - 3);
  227. os_detection_task();
  228. assert_not_reported();
  229. EXPECT_EQ(detected_host_os(), OS_LINUX);
  230. advance_time(1);
  231. os_detection_task();
  232. assert_not_reported();
  233. EXPECT_EQ(detected_host_os(), OS_LINUX);
  234. // advancing the timer alone must not cause a report
  235. advance_time(1);
  236. assert_not_reported();
  237. EXPECT_EQ(detected_host_os(), OS_LINUX);
  238. // the task will cause a report
  239. os_detection_task();
  240. assert_reported(OS_LINUX);
  241. EXPECT_EQ(detected_host_os(), OS_LINUX);
  242. // check that it remains the same after a long time
  243. advance_time(OS_DETECTION_DEBOUNCE * 15);
  244. assert_reported(OS_LINUX);
  245. EXPECT_EQ(detected_host_os(), OS_LINUX);
  246. }
  247. TEST_F(OsDetectionTest, TestReportAfterDebounceLongWait) {
  248. EXPECT_EQ(check_sequence({0x12, 0xFF, 0xFF, 0x4, 0x10, 0xFF, 0xFF, 0xFF, 0x4, 0x10, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A}), OS_WINDOWS);
  249. os_detection_notify_usb_device_state_change(usb_device_state_configured);
  250. os_detection_task();
  251. assert_not_reported();
  252. advance_time(1);
  253. os_detection_task();
  254. assert_not_reported();
  255. EXPECT_EQ(detected_host_os(), OS_WINDOWS);
  256. // advancing the timer alone must not cause a report
  257. advance_time(OS_DETECTION_DEBOUNCE * 15);
  258. assert_not_reported();
  259. EXPECT_EQ(detected_host_os(), OS_WINDOWS);
  260. // the task will cause a report
  261. os_detection_task();
  262. assert_reported(OS_WINDOWS);
  263. EXPECT_EQ(detected_host_os(), OS_WINDOWS);
  264. // check that it remains the same after a long time
  265. advance_time(OS_DETECTION_DEBOUNCE * 10);
  266. os_detection_task();
  267. assert_reported(OS_WINDOWS);
  268. EXPECT_EQ(detected_host_os(), OS_WINDOWS);
  269. }
  270. TEST_F(OsDetectionTest, TestReportUnsure) {
  271. EXPECT_EQ(check_sequence({0x12, 0xFF}), OS_UNSURE);
  272. os_detection_notify_usb_device_state_change(usb_device_state_configured);
  273. os_detection_task();
  274. assert_not_reported();
  275. advance_time(1);
  276. os_detection_task();
  277. assert_not_reported();
  278. EXPECT_EQ(detected_host_os(), OS_UNSURE);
  279. // advancing the timer alone must not cause a report
  280. advance_time(OS_DETECTION_DEBOUNCE - 1);
  281. assert_not_reported();
  282. EXPECT_EQ(detected_host_os(), OS_UNSURE);
  283. // the task will cause a report
  284. os_detection_task();
  285. assert_reported(OS_UNSURE);
  286. EXPECT_EQ(detected_host_os(), OS_UNSURE);
  287. // check that it remains the same after a long time
  288. advance_time(OS_DETECTION_DEBOUNCE * 10);
  289. os_detection_task();
  290. assert_reported(OS_UNSURE);
  291. EXPECT_EQ(detected_host_os(), OS_UNSURE);
  292. }
  293. TEST_F(OsDetectionTest, TestDoNotReportIntermediateResults) {
  294. EXPECT_EQ(check_sequence({0x12, 0xFF}), OS_UNSURE);
  295. os_detection_notify_usb_device_state_change(usb_device_state_configured);
  296. os_detection_task();
  297. assert_not_reported();
  298. advance_time(OS_DETECTION_DEBOUNCE - 1);
  299. os_detection_task();
  300. assert_not_reported();
  301. EXPECT_EQ(detected_host_os(), OS_UNSURE);
  302. // at this stage, the final result has not been reached yet
  303. EXPECT_EQ(check_sequence({0xFF}), OS_LINUX);
  304. os_detection_notify_usb_device_state_change(usb_device_state_configured);
  305. advance_time(OS_DETECTION_DEBOUNCE - 1);
  306. os_detection_task();
  307. assert_not_reported();
  308. // the intermedite but yet unstable result is exposed through detected_host_os()
  309. EXPECT_EQ(detected_host_os(), OS_LINUX);
  310. // the remainder is processed
  311. EXPECT_EQ(check_sequence({0x4, 0x10, 0xFF, 0xFF, 0xFF, 0x4, 0x10, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A}), OS_WINDOWS);
  312. os_detection_notify_usb_device_state_change(usb_device_state_configured);
  313. advance_time(OS_DETECTION_DEBOUNCE - 1);
  314. os_detection_task();
  315. assert_not_reported();
  316. EXPECT_EQ(detected_host_os(), OS_WINDOWS);
  317. // advancing the timer alone must not cause a report
  318. advance_time(1);
  319. assert_not_reported();
  320. EXPECT_EQ(detected_host_os(), OS_WINDOWS);
  321. // the task will cause a report
  322. os_detection_task();
  323. assert_reported(OS_WINDOWS);
  324. EXPECT_EQ(detected_host_os(), OS_WINDOWS);
  325. // check that it remains the same after a long time
  326. advance_time(OS_DETECTION_DEBOUNCE * 10);
  327. os_detection_task();
  328. assert_reported(OS_WINDOWS);
  329. EXPECT_EQ(detected_host_os(), OS_WINDOWS);
  330. }
  331. TEST_F(OsDetectionTest, TestDoNotGoBackToUnsure) {
  332. // 0x02 would cause it to go back to Unsure, so check that it does not
  333. EXPECT_EQ(check_sequence({0xFF, 0xFF, 0xFF, 0xFE, 0x02}), OS_LINUX);
  334. os_detection_task();
  335. assert_not_reported();
  336. }