85ee55ff3becd29e1590376c3462c6a99c976fa0
- Author
- Ruslan Sayfutdinov <ruslan@sayfutdinov.com>
- Committer
- GitHub <noreply@github.com>
- Date
Message
Diff
This diff is truncated to protect this page.
1diff --git a/builddefs/build_test.mk b/builddefs/build_test.mk
2index 7c8fdd20e5ae4169e62553d4292a743c68a9a758..42305983739f5a9878463b059f655d980f6ba816 100644
3--- a/builddefs/build_test.mk
4+++ b/builddefs/build_test.mk
5@@ -62,6 +62,7 @@ include $(PLATFORM_PATH)/common.mk
6 include $(TMK_PATH)/protocol.mk
7 include $(QUANTUM_PATH)/debounce/tests/rules.mk
8 include $(QUANTUM_PATH)/encoder/tests/rules.mk
9+include $(QUANTUM_PATH)/os_detection/tests/rules.mk
10 include $(QUANTUM_PATH)/sequencer/tests/rules.mk
11 include $(QUANTUM_PATH)/wear_leveling/tests/rules.mk
12 include $(QUANTUM_PATH)/logging/print.mk
13diff --git a/builddefs/common_features.mk b/builddefs/common_features.mk
14index d9130b533805980e8d5dcb76d37692ee9ed2c11d..dbcfcb7145d56d8bad69be05301d8e551a122d46 100644
15--- a/builddefs/common_features.mk
16+++ b/builddefs/common_features.mk
17@@ -907,3 +907,11 @@ ifeq ($(strip $(ENCODER_ENABLE)), yes)
18 OPT_DEFS += -DENCODER_MAP_ENABLE
19 endif
20 endif
21+
22+ifeq ($(strip $(OS_DETECTION_ENABLE)), yes)
23+ SRC += $(QUANTUM_DIR)/os_detection.c
24+ OPT_DEFS += -DOS_DETECTION_ENABLE
25+ ifeq ($(strip $(OS_DETECTION_DEBUG_ENABLE)), yes)
26+ OPT_DEFS += -DOS_DETECTION_DEBUG_ENABLE
27+ endif
28+endif
29diff --git a/builddefs/testlist.mk b/builddefs/testlist.mk
30index 8a30a44972425e923ea5acaa4661010cc979cda6..74a794adcdc9646a364564815332e584391bbe50 100644
31--- a/builddefs/testlist.mk
32+++ b/builddefs/testlist.mk
33@@ -3,6 +3,7 @@ FULL_TESTS := $(notdir $(TEST_LIST))
34
35 include $(QUANTUM_PATH)/debounce/tests/testlist.mk
36 include $(QUANTUM_PATH)/encoder/tests/testlist.mk
37+include $(QUANTUM_PATH)/os_detection/tests/testlist.mk
38 include $(QUANTUM_PATH)/sequencer/tests/testlist.mk
39 include $(QUANTUM_PATH)/wear_leveling/tests/testlist.mk
40 include $(PLATFORM_PATH)/test/testlist.mk
41diff --git a/docs/_summary.md b/docs/_summary.md
42index 738c24ee42e62bfb6deba7e826a002a6631c555b..d388d08391b32af2b3d6a0e9c92987102f87feaf 100644
43--- a/docs/_summary.md
44+++ b/docs/_summary.md
45@@ -85,6 +85,7 @@
46 * [Key Overrides](feature_key_overrides.md)
47 * [Layers](feature_layers.md)
48 * [One Shot Keys](one_shot_keys.md)
49+ * [OS Detection](feature_os_detection.md)
50 * [Raw HID](feature_rawhid.md)
51 * [Secure](feature_secure.md)
52 * [Send String](feature_send_string.md)
53diff --git a/docs/feature_os_detection.md b/docs/feature_os_detection.md
54new file mode 100644
55index 0000000000000000000000000000000000000000..f32e419807f7f1d09993b64375ef43c9e959f32a
56--- /dev/null
57+++ b/docs/feature_os_detection.md
58@@ -0,0 +1,77 @@
59+# OS Detection
60+
61+This feature makes a best guess at the host OS based on OS specific behavior during USB setup. It may not always get the correct OS, and shouldn't be relied on as for critical functionality.
62+
63+Using it you can have OS specific key mappings or combos which work differently on different devices.
64+
65+It is available for keyboards which use ChibiOS, LUFA and V-USB.
66+
67+## Usage
68+
69+In your `rules.mk` add:
70+
71+```make
72+OS_DETECTION_ENABLE = yes
73+```
74+
75+Include `"os_detection.h"` in your `keymap.c`.
76+It declares `os_variant_t detected_host_os(void);` which you can call to get detected OS.
77+
78+It returns one of the following values:
79+
80+```c
81+enum {
82+ OS_UNSURE,
83+ OS_LINUX,
84+ OS_WINDOWS,
85+ OS_MACOS,
86+ OS_IOS,
87+} os_variant_t;
88+```
89+
90+?> Note that it takes some time after firmware is booted to detect the OS.
91+This time is quite short, probably hundreds of milliseconds, but this data may be not ready in keyboard and layout setup functions which run very early during firmware startup.
92+
93+## Debug
94+
95+If OS is guessed incorrectly, you may want to collect data about USB setup packets to refine the detection logic.
96+
97+To do so in your `rules.mk` add:
98+
99+```make
100+OS_DETECTION_DEBUG_ENABLE = yes
101+CONSOLE_ENABLE = yes
102+```
103+
104+And also include `"os_detection.h"` in your `keymap.c`.
105+
106+Then you can define custom keycodes to store data about USB setup packets in EEPROM (persistent memory) and to print it later on host where you can run `qmk console`:
107+
108+```c
109+enum custom_keycodes {
110+ STORE_SETUPS = SAFE_RANGE,
111+ PRINT_SETUPS,
112+};
113+
114+bool process_record_user(uint16_t keycode, keyrecord_t *record) {
115+ switch (keycode) {
116+ case STORE_SETUPS:
117+ if (record->event.pressed) {
118+ store_setups_in_eeprom();
119+ }
120+ return false;
121+ case PRINT_SETUPS:
122+ if (record->event.pressed) {
123+ print_stored_setups();
124+ }
125+ return false;
126+ }
127+}
128+```
129+
130+Then please open an issue on Github with this information and tell what OS was not detected correctly and if you have any intermediate devices between keyboard and your computer.
131+
132+
133+## Credits
134+
135+Original idea is coming from [FingerprintUSBHost](https://github.com/keyboardio/FingerprintUSBHost) project.
136diff --git a/quantum/os_detection.c b/quantum/os_detection.c
137new file mode 100644
138index 0000000000000000000000000000000000000000..b1511afb149a22d90ab27e1be447e35f21fcf70a
139--- /dev/null
140+++ b/quantum/os_detection.c
141@@ -0,0 +1,129 @@
142+/* Copyright 2022 Ruslan Sayfutdinov (@KapJI)
143+ *
144+ * This program is free software: you can redistribute it and/or modify
145+ * it under the terms of the GNU General Public License as published by
146+ * the Free Software Foundation, either version 2 of the License, or
147+ * (at your option) any later version.
148+ *
149+ * This program is distributed in the hope that it will be useful,
150+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
151+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
152+ * GNU General Public License for more details.
153+ *
154+ * You should have received a copy of the GNU General Public License
155+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
156+ */
157+
158+#include "os_detection.h"
159+
160+#include <string.h>
161+
162+#ifdef OS_DETECTION_DEBUG_ENABLE
163+# include "eeconfig.h"
164+# include "eeprom.h"
165+# include "print.h"
166+
167+# define STORED_USB_SETUPS 50
168+# define EEPROM_USER_OFFSET (uint8_t*)EECONFIG_SIZE
169+
170+uint16_t usb_setups[STORED_USB_SETUPS];
171+#endif
172+
173+#ifdef OS_DETECTION_ENABLE
174+struct setups_data_t {
175+ uint8_t count;
176+ uint8_t cnt_02;
177+ uint8_t cnt_04;
178+ uint8_t cnt_ff;
179+ uint16_t last_wlength;
180+ os_variant_t detected_os;
181+};
182+
183+struct setups_data_t setups_data = {
184+ .count = 0,
185+ .cnt_02 = 0,
186+ .cnt_04 = 0,
187+ .cnt_ff = 0,
188+ .detected_os = OS_UNSURE,
189+};
190+
191+// Some collected sequences of wLength can be found in tests.
192+void make_guess(void) {
193+ if (setups_data.count < 3) {
194+ return;
195+ }
196+ if (setups_data.cnt_ff >= 2 && setups_data.cnt_04 >= 1) {
197+ setups_data.detected_os = OS_WINDOWS;
198+ return;
199+ }
200+ if (setups_data.count == setups_data.cnt_ff) {
201+ // Linux has 3 packets with 0xFF.
202+ setups_data.detected_os = OS_LINUX;
203+ return;
204+ }
205+ if (setups_data.count == 5 && setups_data.last_wlength == 0xFF && setups_data.cnt_ff == 1 && setups_data.cnt_02 == 2) {
206+ setups_data.detected_os = OS_MACOS;
207+ return;
208+ }
209+ if (setups_data.count == 4 && setups_data.cnt_ff == 0 && setups_data.cnt_02 == 2) {
210+ // iOS and iPadOS don't have the last 0xFF packet.
211+ setups_data.detected_os = OS_IOS;
212+ return;
213+ }
214+ if (setups_data.cnt_ff == 0 && setups_data.cnt_02 == 3 && setups_data.cnt_04 == 1) {
215+ // This is actually PS5.
216+ setups_data.detected_os = OS_LINUX;
217+ return;
218+ }
219+ if (setups_data.cnt_ff >= 1 && setups_data.cnt_02 == 0 && setups_data.cnt_04 == 0) {
220+ // This is actually Quest 2 or Nintendo Switch.
221+ setups_data.detected_os = OS_LINUX;
222+ return;
223+ }
224+}
225+
226+void process_wlength(const uint16_t w_length) {
227+# ifdef OS_DETECTION_DEBUG_ENABLE
228+ usb_setups[setups_data.count] = w_length;
229+# endif
230+ setups_data.count++;
231+ setups_data.last_wlength = w_length;
232+ if (w_length == 0x2) {
233+ setups_data.cnt_02++;
234+ } else if (w_length == 0x4) {
235+ setups_data.cnt_04++;
236+ } else if (w_length == 0xFF) {
237+ setups_data.cnt_ff++;
238+ }
239+ make_guess();
240+}
241diff --git a/quantum/os_detection.h b/quantum/os_detection.h
242new file mode 100644
243index 0000000000000000000000000000000000000000..e643dcd27fabcdd4fe3022dec866d47f2b77b398
244--- /dev/null
245+++ b/quantum/os_detection.h
246@@ -0,0 +1,38 @@
247+/* Copyright 2022 Ruslan Sayfutdinov (@KapJI)
248+ *
249+ * This program is free software: you can redistribute it and/or modify
250+ * it under the terms of the GNU General Public License as published by
251+ * the Free Software Foundation, either version 2 of the License, or
252+ * (at your option) any later version.
253+ *
254+ * This program is distributed in the hope that it will be useful,
255+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
256+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
257+ * GNU General Public License for more details.
258+ *
259+ * You should have received a copy of the GNU General Public License
260+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
261+ */
262+
263+#pragma once
264+
265+#include <stdint.h>
266+
267+#ifdef OS_DETECTION_ENABLE
268+typedef enum {
269+ OS_UNSURE,
270+ OS_LINUX,
271+ OS_WINDOWS,
272+ OS_MACOS,
273+ OS_IOS,
274+} os_variant_t;
275+
276+void process_wlength(const uint16_t w_length);
277+os_variant_t detected_host_os(void);
278+void erase_wlength_data(void);
279+#endif
280+
281+#ifdef OS_DETECTION_DEBUG_ENABLE
282+void print_stored_setups(void);
283+void store_setups_in_eeprom(void);
284+#endif
285diff --git a/quantum/os_detection/tests/os_detection.cpp b/quantum/os_detection/tests/os_detection.cpp
286new file mode 100644
287index 0000000000000000000000000000000000000000..102349852e0c4b7381ac0d4ccf627a72249c9631
288--- /dev/null
289+++ b/quantum/os_detection/tests/os_detection.cpp
290@@ -0,0 +1,164 @@
291+/* Copyright 2022 Ruslan Sayfutdinov (@KapJI)
292+ *
293+ * This program is free software: you can redistribute it and/or modify
294+ * it under the terms of the GNU General Public License as published by
295+ * the Free Software Foundation, either version 2 of the License, or
296+ * (at your option) any later version.
297+ *
298+ * This program is distributed in the hope that it will be useful,
299+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
300+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
301+ * GNU General Public License for more details.
302+ *
303+ * You should have received a copy of the GNU General Public License
304+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
305+ */
306+
307+#include "gtest/gtest.h"
308+
309+extern "C" {
310+#include "os_detection.h"
311+}
312+
313+class OsDetectionTest : public ::testing::Test {
314+ protected:
315+ void SetUp() override {
316+ erase_wlength_data();
317+ }
318+};
319+
320+os_variant_t check_sequence(const std::vector<uint16_t> &w_lengths) {
321+ for (auto &w_length : w_lengths) {
322+ process_wlength(w_length);
323+ }
324+ return detected_host_os();
325+}
326+
327+/* Some collected data.
328+
329+ChibiOS:
330+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]
331+Windows 10 (another host): [FF, FF, 4, 24, 4, 24, 4, 24, 4, 24, 4, 24]
332+macOS 12.5: [2, 24, 2, 28, FF]
333+iOS/iPadOS 15.6: [2, 24, 2, 28]
334+Linux (including Android, Raspberry Pi and WebOS TV): [FF, FF, FF]
335+PS5: [2, 4, 2, 28, 2, 24]
336+Nintendo Switch: [82, FF, 40, 40, FF, 40, 40, FF, 40, 40, FF, 40, 40, FF, 40, 40]
337+Quest 2: [FF, FF, FF, FE, FF, FE, FF, FE, FF, FE, FF]
338+
339+LUFA:
340+Windows 10 (first connect): [12, FF, FF, 4, 10, FF, FF, FF, 4, 10, 20A, 20A, 20A, 20A, 20A, 20A]
341+Windows 10 (subsequent connect): [FF, FF, 4, 10, FF, 4, FF, 10, FF, 20A, 20A, 20A, 20A, 20A, 20A]
342+Windows 10 (another host): [FF, FF, 4, 10, 4, 10]
343+macOS: [2, 10, 2, E, FF]
344+iOS/iPadOS: [2, 10, 2, E]
345+Linux: [FF, FF, FF]
346+PS5: [2, 4, 2, E, 2, 10]
347+Nintendo Switch: [82, FF, 40, 40, FF, 40, 40]
348+
349+V-USB:
350+Windows 10: [FF, FF, 4, E, FF]
351+Windows 10 (another host): [FF, FF, 4, E, 4]
352+macOS: [2, E, 2, E, FF]
353+iOS/iPadOS: [2, E, 2, E]
354+Linux: [FF, FF, FF]
355+PS5: [2, 4, 2, E, 2]
356+Nintendo Switch: [82, FF, 40, 40]
357+Quest 2: [FF, FF, FF, FE]
358+
359+Common parts:
360+Windows: [..., FF, FF, 4, ...]
361+macOS: [2, _, 2, _, FF]
362+iOS/iPadOS: [2, _, 2, _]
363+Linux: [FF, FF, FF]
364+PS5: [2, 4, 2, _, 2, ...]
365+Nintendo Switch: [82, FF, 40, 40, ...]
366+Quest 2: [FF, FF, FF, FE, ...]
367+*/
368+TEST_F(OsDetectionTest, TestLinux) {
369+ EXPECT_EQ(check_sequence({0xFF, 0xFF, 0xFF}), OS_LINUX);
370+}
371+
372+TEST_F(OsDetectionTest, TestChibiosMacos) {
373+ EXPECT_EQ(check_sequence({0x2, 0x24, 0x2, 0x28, 0xFF}), OS_MACOS);
374+}
375+
376+TEST_F(OsDetectionTest, TestLufaMacos) {
377+ EXPECT_EQ(check_sequence({0x2, 0x10, 0x2, 0xE, 0xFF}), OS_MACOS);
378+}
379+
380+TEST_F(OsDetectionTest, TestVusbMacos) {
381+ EXPECT_EQ(check_sequence({0x2, 0xE, 0x2, 0xE, 0xFF}), OS_MACOS);
382+}
383+
384+TEST_F(OsDetectionTest, TestChibiosIos) {
385+ EXPECT_EQ(check_sequence({0x2, 0x24, 0x2, 0x28}), OS_IOS);
386+}
387+
388+TEST_F(OsDetectionTest, TestLufaIos) {
389+ EXPECT_EQ(check_sequence({0x2, 0x10, 0x2, 0xE}), OS_IOS);
390diff --git a/quantum/os_detection/tests/rules.mk b/quantum/os_detection/tests/rules.mk
391new file mode 100644
392index 0000000000000000000000000000000000000000..9bfe373f46ef54ec566a21c25ccc270503e73bea
393--- /dev/null
394+++ b/quantum/os_detection/tests/rules.mk
395@@ -0,0 +1,5 @@
396+os_detection_DEFS := -DOS_DETECTION_ENABLE
397+
398+os_detection_SRC := \
399+ $(QUANTUM_PATH)/os_detection/tests/os_detection.cpp \
400+ $(QUANTUM_PATH)/os_detection.c
401diff --git a/quantum/os_detection/tests/testlist.mk b/quantum/os_detection/tests/testlist.mk
402new file mode 100644
403index 0000000000000000000000000000000000000000..405a7b82d55b250925cae122d6c0b4b694f61e14
404--- /dev/null
405+++ b/quantum/os_detection/tests/testlist.mk
406@@ -0,0 +1 @@
407+TEST_LIST += os_detection
408diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c
409index 094c14d1b3a3a1979529c488adb346ca04aceef4..5d36900bddcde470d6db1e76fb7e89c893ce3388 100644
410--- a/tmk_core/protocol/chibios/usb_main.c
411+++ b/tmk_core/protocol/chibios/usb_main.c
412@@ -111,9 +111,10 @@ uint8_t extra_report_blank[3] = {0};
413 static const USBDescriptor *usb_get_descriptor_cb(USBDriver *usbp, uint8_t dtype, uint8_t dindex, uint16_t wIndex) {
414 (void)usbp;
415 static USBDescriptor desc;
416- uint16_t wValue = ((uint16_t)dtype << 8) | dindex;
417- desc.ud_string = NULL;
418- desc.ud_size = get_usb_descriptor(wValue, wIndex, (const void **const) & desc.ud_string);
419+ uint16_t wValue = ((uint16_t)dtype << 8) | dindex;
420+ uint16_t wLength = ((uint16_t)usbp->setup[7] << 8) | usbp->setup[6];
421+ desc.ud_string = NULL;
422+ desc.ud_size = get_usb_descriptor(wValue, wIndex, wLength, (const void **const) & desc.ud_string);
423 if (desc.ud_string == NULL)
424 return NULL;
425 else
426@@ -585,7 +586,8 @@ static uint16_t get_hword(uint8_t *p) {
427 */
428
429 static uint8_t set_report_buf[2] __attribute__((aligned(4)));
430-static void set_led_transfer_cb(USBDriver *usbp) {
431+
432+static void set_led_transfer_cb(USBDriver *usbp) {
433 if (usbp->setup[6] == 2) { /* LSB(wLength) */
434 uint8_t report_id = set_report_buf[0];
435 if ((report_id == REPORT_ID_KEYBOARD) || (report_id == REPORT_ID_NKRO)) {
436diff --git a/tmk_core/protocol/lufa/lufa.c b/tmk_core/protocol/lufa/lufa.c
437index 8f36e02b9aaeba9f24aa27aa834b0acf98075f0a..99f7d312875dacc3422e6d94e69f0a0d92297787 100644
438--- a/tmk_core/protocol/lufa/lufa.c
439+++ b/tmk_core/protocol/lufa/lufa.c
440@@ -901,5 +901,5 @@ void protocol_post_task(void) {
441 }
442
443 uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue, const uint16_t wIndex, const void **const DescriptorAddress) {
444- return get_usb_descriptor(wValue, wIndex, DescriptorAddress);
445+ return get_usb_descriptor(wValue, wIndex, USB_ControlRequest.wLength, DescriptorAddress);
446 }
447diff --git a/tmk_core/protocol/usb_descriptor.c b/tmk_core/protocol/usb_descriptor.c
448index c7d78be8565fe499db58a0b4d5a4730bc1ae2dde..5ab9e3ff4f56164a101c326a9278c73a4c3d5692 100644
449--- a/tmk_core/protocol/usb_descriptor.c
450+++ b/tmk_core/protocol/usb_descriptor.c
451@@ -45,6 +45,10 @@
452 # include "joystick.h"
453 #endif
454
455+#ifdef OS_DETECTION_ENABLE
456+# include "os_detection.h"
457+#endif
458+
459 // clang-format off
460
461 /*
462@@ -1092,7 +1096,7 @@ const USB_Descriptor_String_t PROGMEM SerialNumberString = {
463 * is called so that the descriptor details can be passed back and the appropriate descriptor sent back to the
464 * USB host.
465 */
466-uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const void** const DescriptorAddress) {
467+uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const uint16_t wLength, const void** const DescriptorAddress) {
468 const uint8_t DescriptorType = (wValue >> 8);
469 const uint8_t DescriptorIndex = (wValue & 0xFF);
470 const void* Address = NULL;
471@@ -1134,6 +1138,9 @@ uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const
472 break;
473 #endif
474 }
475+#ifdef OS_DETECTION_ENABLE
476+ process_wlength(wLength);
477+#endif
478
479 break;
480 case HID_DTYPE_HID:
481diff --git a/tmk_core/protocol/usb_descriptor.h b/tmk_core/protocol/usb_descriptor.h
482index bc5e84e586ef0586a10f166b7a52b32bf896266e..ed84f4c9ab55bae27c320c29890c618236180424 100644
483--- a/tmk_core/protocol/usb_descriptor.h
484+++ b/tmk_core/protocol/usb_descriptor.h
485@@ -308,4 +308,4 @@ enum usb_endpoints {
486 #define JOYSTICK_EPSIZE 8
487 #define DIGITIZER_EPSIZE 8
488
489-uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const void** const DescriptorAddress);
490+uint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const uint16_t wLength, const void** const DescriptorAddress);
491diff --git a/tmk_core/protocol/vusb/vusb.c b/tmk_core/protocol/vusb/vusb.c
492index 2d17761978edbfc05c39dac73752488cbf063ab1..b3eeff9e0169c12047235f66a3634e821e81be23 100644
493--- a/tmk_core/protocol/vusb/vusb.c
494+++ b/tmk_core/protocol/vusb/vusb.c
495@@ -44,6 +44,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
496 # include "ring_buffer.h"
497 #endif
498
499+#ifdef OS_DETECTION_ENABLE
500+# include "os_detection.h"
501+#endif
502+
503 #define NEXT_INTERFACE __COUNTER__
504
505 /*
506@@ -1013,6 +1017,9 @@ USB_PUBLIC usbMsgLen_t usbFunctionDescriptor(struct usbRequest *rq) {
507 break;
508 #endif
509 }
510+#ifdef OS_DETECTION_ENABLE
511+ process_wlength(rq->wLength.word);
512+#endif
513 break;
514 case USBDESCR_HID:
515 switch (rq->wValue.bytes[0]) {