Detect host OS based on USB fingerprint (#18463)
Co-authored-by: Drashna Jaelre <drashna@live.com> Co-authored-by: Nick Brassel <nick@tzarc.org>
This commit is contained in:
		
							parent
							
								
									e06f50c489
								
							
						
					
					
						commit
						85ee55ff3b
					
				
					 15 changed files with 448 additions and 7 deletions
				
			
		
							
								
								
									
										129
									
								
								quantum/os_detection.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								quantum/os_detection.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,129 @@
 | 
			
		|||
/* Copyright 2022 Ruslan Sayfutdinov (@KapJI)
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 2 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "os_detection.h"
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#ifdef OS_DETECTION_DEBUG_ENABLE
 | 
			
		||||
#    include "eeconfig.h"
 | 
			
		||||
#    include "eeprom.h"
 | 
			
		||||
#    include "print.h"
 | 
			
		||||
 | 
			
		||||
#    define STORED_USB_SETUPS 50
 | 
			
		||||
#    define EEPROM_USER_OFFSET (uint8_t*)EECONFIG_SIZE
 | 
			
		||||
 | 
			
		||||
uint16_t usb_setups[STORED_USB_SETUPS];
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef OS_DETECTION_ENABLE
 | 
			
		||||
struct setups_data_t {
 | 
			
		||||
    uint8_t      count;
 | 
			
		||||
    uint8_t      cnt_02;
 | 
			
		||||
    uint8_t      cnt_04;
 | 
			
		||||
    uint8_t      cnt_ff;
 | 
			
		||||
    uint16_t     last_wlength;
 | 
			
		||||
    os_variant_t detected_os;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct setups_data_t setups_data = {
 | 
			
		||||
    .count       = 0,
 | 
			
		||||
    .cnt_02      = 0,
 | 
			
		||||
    .cnt_04      = 0,
 | 
			
		||||
    .cnt_ff      = 0,
 | 
			
		||||
    .detected_os = OS_UNSURE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Some collected sequences of wLength can be found in tests.
 | 
			
		||||
void make_guess(void) {
 | 
			
		||||
    if (setups_data.count < 3) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    if (setups_data.cnt_ff >= 2 && setups_data.cnt_04 >= 1) {
 | 
			
		||||
        setups_data.detected_os = OS_WINDOWS;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    if (setups_data.count == setups_data.cnt_ff) {
 | 
			
		||||
        // Linux has 3 packets with 0xFF.
 | 
			
		||||
        setups_data.detected_os = OS_LINUX;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    if (setups_data.count == 5 && setups_data.last_wlength == 0xFF && setups_data.cnt_ff == 1 && setups_data.cnt_02 == 2) {
 | 
			
		||||
        setups_data.detected_os = OS_MACOS;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    if (setups_data.count == 4 && setups_data.cnt_ff == 0 && setups_data.cnt_02 == 2) {
 | 
			
		||||
        // iOS and iPadOS don't have the last 0xFF packet.
 | 
			
		||||
        setups_data.detected_os = OS_IOS;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    if (setups_data.cnt_ff == 0 && setups_data.cnt_02 == 3 && setups_data.cnt_04 == 1) {
 | 
			
		||||
        // This is actually PS5.
 | 
			
		||||
        setups_data.detected_os = OS_LINUX;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    if (setups_data.cnt_ff >= 1 && setups_data.cnt_02 == 0 && setups_data.cnt_04 == 0) {
 | 
			
		||||
        // This is actually Quest 2 or Nintendo Switch.
 | 
			
		||||
        setups_data.detected_os = OS_LINUX;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void process_wlength(const uint16_t w_length) {
 | 
			
		||||
#    ifdef OS_DETECTION_DEBUG_ENABLE
 | 
			
		||||
    usb_setups[setups_data.count] = w_length;
 | 
			
		||||
#    endif
 | 
			
		||||
    setups_data.count++;
 | 
			
		||||
    setups_data.last_wlength = w_length;
 | 
			
		||||
    if (w_length == 0x2) {
 | 
			
		||||
        setups_data.cnt_02++;
 | 
			
		||||
    } else if (w_length == 0x4) {
 | 
			
		||||
        setups_data.cnt_04++;
 | 
			
		||||
    } else if (w_length == 0xFF) {
 | 
			
		||||
        setups_data.cnt_ff++;
 | 
			
		||||
    }
 | 
			
		||||
    make_guess();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
os_variant_t detected_host_os(void) {
 | 
			
		||||
    return setups_data.detected_os;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void erase_wlength_data(void) {
 | 
			
		||||
    memset(&setups_data, 0, sizeof(setups_data));
 | 
			
		||||
}
 | 
			
		||||
#endif // OS_DETECTION_ENABLE
 | 
			
		||||
 | 
			
		||||
#ifdef OS_DETECTION_DEBUG_ENABLE
 | 
			
		||||
void print_stored_setups(void) {
 | 
			
		||||
#    ifdef CONSOLE_ENABLE
 | 
			
		||||
    uint8_t cnt = eeprom_read_byte(EEPROM_USER_OFFSET);
 | 
			
		||||
    for (uint16_t i = 0; i < cnt; ++i) {
 | 
			
		||||
        uint16_t* addr = (uint16_t*)EEPROM_USER_OFFSET + i * sizeof(uint16_t) + sizeof(uint8_t);
 | 
			
		||||
        xprintf("i: %d, wLength: 0x%02X\n", i, eeprom_read_word(addr));
 | 
			
		||||
    }
 | 
			
		||||
#    endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void store_setups_in_eeprom(void) {
 | 
			
		||||
    eeprom_update_byte(EEPROM_USER_OFFSET, setups_data.count);
 | 
			
		||||
    for (uint16_t i = 0; i < setups_data.count; ++i) {
 | 
			
		||||
        uint16_t* addr = (uint16_t*)EEPROM_USER_OFFSET + i * sizeof(uint16_t) + sizeof(uint8_t);
 | 
			
		||||
        eeprom_update_word(addr, usb_setups[i]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif // OS_DETECTION_DEBUG_ENABLE
 | 
			
		||||
							
								
								
									
										38
									
								
								quantum/os_detection.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								quantum/os_detection.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,38 @@
 | 
			
		|||
/* Copyright 2022 Ruslan Sayfutdinov (@KapJI)
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 2 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
#ifdef OS_DETECTION_ENABLE
 | 
			
		||||
typedef enum {
 | 
			
		||||
    OS_UNSURE,
 | 
			
		||||
    OS_LINUX,
 | 
			
		||||
    OS_WINDOWS,
 | 
			
		||||
    OS_MACOS,
 | 
			
		||||
    OS_IOS,
 | 
			
		||||
} os_variant_t;
 | 
			
		||||
 | 
			
		||||
void         process_wlength(const uint16_t w_length);
 | 
			
		||||
os_variant_t detected_host_os(void);
 | 
			
		||||
void         erase_wlength_data(void);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef OS_DETECTION_DEBUG_ENABLE
 | 
			
		||||
void print_stored_setups(void);
 | 
			
		||||
void store_setups_in_eeprom(void);
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										164
									
								
								quantum/os_detection/tests/os_detection.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								quantum/os_detection/tests/os_detection.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,164 @@
 | 
			
		|||
/* Copyright 2022 Ruslan Sayfutdinov (@KapJI)
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 2 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "gtest/gtest.h"
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include "os_detection.h"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class OsDetectionTest : public ::testing::Test {
 | 
			
		||||
   protected:
 | 
			
		||||
    void SetUp() override {
 | 
			
		||||
        erase_wlength_data();
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
os_variant_t check_sequence(const std::vector<uint16_t> &w_lengths) {
 | 
			
		||||
    for (auto &w_length : w_lengths) {
 | 
			
		||||
        process_wlength(w_length);
 | 
			
		||||
    }
 | 
			
		||||
    return detected_host_os();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Some collected data.
 | 
			
		||||
 | 
			
		||||
ChibiOS:
 | 
			
		||||
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]
 | 
			
		||||
Windows 10 (another host): [FF, FF, 4, 24, 4, 24, 4, 24, 4, 24, 4, 24]
 | 
			
		||||
macOS 12.5: [2, 24, 2, 28, FF]
 | 
			
		||||
iOS/iPadOS 15.6: [2, 24, 2, 28]
 | 
			
		||||
Linux (including Android, Raspberry Pi and WebOS TV): [FF, FF, FF]
 | 
			
		||||
PS5: [2, 4, 2, 28, 2, 24]
 | 
			
		||||
Nintendo Switch: [82, FF, 40, 40, FF, 40, 40, FF, 40, 40, FF, 40, 40, FF, 40, 40]
 | 
			
		||||
Quest 2: [FF, FF, FF, FE, FF, FE, FF, FE, FF, FE, FF]
 | 
			
		||||
 | 
			
		||||
LUFA:
 | 
			
		||||
Windows 10 (first connect): [12, FF, FF, 4, 10, FF, FF, FF, 4, 10, 20A, 20A, 20A, 20A, 20A, 20A]
 | 
			
		||||
Windows 10 (subsequent connect): [FF, FF, 4, 10, FF, 4, FF, 10, FF, 20A, 20A, 20A, 20A, 20A, 20A]
 | 
			
		||||
Windows 10 (another host): [FF, FF, 4, 10, 4, 10]
 | 
			
		||||
macOS: [2, 10, 2, E, FF]
 | 
			
		||||
iOS/iPadOS: [2, 10, 2, E]
 | 
			
		||||
Linux: [FF, FF, FF]
 | 
			
		||||
PS5: [2, 4, 2, E, 2, 10]
 | 
			
		||||
Nintendo Switch: [82, FF, 40, 40, FF, 40, 40]
 | 
			
		||||
 | 
			
		||||
V-USB:
 | 
			
		||||
Windows 10: [FF, FF, 4, E, FF]
 | 
			
		||||
Windows 10 (another host): [FF, FF, 4, E, 4]
 | 
			
		||||
macOS: [2, E, 2, E, FF]
 | 
			
		||||
iOS/iPadOS: [2, E, 2, E]
 | 
			
		||||
Linux: [FF, FF, FF]
 | 
			
		||||
PS5: [2, 4, 2, E, 2]
 | 
			
		||||
Nintendo Switch: [82, FF, 40, 40]
 | 
			
		||||
Quest 2: [FF, FF, FF, FE]
 | 
			
		||||
 | 
			
		||||
Common parts:
 | 
			
		||||
Windows: [..., FF, FF, 4, ...]
 | 
			
		||||
macOS: [2, _, 2, _, FF]
 | 
			
		||||
iOS/iPadOS: [2, _, 2, _]
 | 
			
		||||
Linux: [FF, FF, FF]
 | 
			
		||||
PS5: [2, 4, 2, _, 2, ...]
 | 
			
		||||
Nintendo Switch: [82, FF, 40, 40, ...]
 | 
			
		||||
Quest 2: [FF, FF, FF, FE, ...]
 | 
			
		||||
*/
 | 
			
		||||
TEST_F(OsDetectionTest, TestLinux) {
 | 
			
		||||
    EXPECT_EQ(check_sequence({0xFF, 0xFF, 0xFF}), OS_LINUX);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(OsDetectionTest, TestChibiosMacos) {
 | 
			
		||||
    EXPECT_EQ(check_sequence({0x2, 0x24, 0x2, 0x28, 0xFF}), OS_MACOS);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(OsDetectionTest, TestLufaMacos) {
 | 
			
		||||
    EXPECT_EQ(check_sequence({0x2, 0x10, 0x2, 0xE, 0xFF}), OS_MACOS);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(OsDetectionTest, TestVusbMacos) {
 | 
			
		||||
    EXPECT_EQ(check_sequence({0x2, 0xE, 0x2, 0xE, 0xFF}), OS_MACOS);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(OsDetectionTest, TestChibiosIos) {
 | 
			
		||||
    EXPECT_EQ(check_sequence({0x2, 0x24, 0x2, 0x28}), OS_IOS);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(OsDetectionTest, TestLufaIos) {
 | 
			
		||||
    EXPECT_EQ(check_sequence({0x2, 0x10, 0x2, 0xE}), OS_IOS);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(OsDetectionTest, TestVusbIos) {
 | 
			
		||||
    EXPECT_EQ(check_sequence({0x2, 0xE, 0x2, 0xE}), OS_IOS);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(OsDetectionTest, TestChibiosWindows10) {
 | 
			
		||||
    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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(OsDetectionTest, TestChibiosWindows10_2) {
 | 
			
		||||
    EXPECT_EQ(check_sequence({0xFF, 0xFF, 0x4, 0x24, 0x4, 0x24, 0x4, 0x24, 0x4, 0x24, 0x4, 0x24}), OS_WINDOWS);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(OsDetectionTest, TestLufaWindows10) {
 | 
			
		||||
    EXPECT_EQ(check_sequence({0x12, 0xFF, 0xFF, 0x4, 0x10, 0xFF, 0xFF, 0xFF, 0x4, 0x10, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A}), OS_WINDOWS);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(OsDetectionTest, TestLufaWindows10_2) {
 | 
			
		||||
    EXPECT_EQ(check_sequence({0xFF, 0xFF, 0x4, 0x10, 0xFF, 0x4, 0xFF, 0x10, 0xFF, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A}), OS_WINDOWS);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(OsDetectionTest, TestLufaWindows10_3) {
 | 
			
		||||
    EXPECT_EQ(check_sequence({0xFF, 0xFF, 0x4, 0x10, 0x4, 0x10}), OS_WINDOWS);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(OsDetectionTest, TestVusbWindows10) {
 | 
			
		||||
    EXPECT_EQ(check_sequence({0xFF, 0xFF, 0x4, 0xE, 0xFF}), OS_WINDOWS);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(OsDetectionTest, TestVusbWindows10_2) {
 | 
			
		||||
    EXPECT_EQ(check_sequence({0xFF, 0xFF, 0x4, 0xE, 0x4}), OS_WINDOWS);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(OsDetectionTest, TestChibiosPs5) {
 | 
			
		||||
    EXPECT_EQ(check_sequence({0x2, 0x4, 0x2, 0x28, 0x2, 0x24}), OS_LINUX);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(OsDetectionTest, TestLufaPs5) {
 | 
			
		||||
    EXPECT_EQ(check_sequence({0x2, 0x4, 0x2, 0xE, 0x2, 0x10}), OS_LINUX);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(OsDetectionTest, TestVusbPs5) {
 | 
			
		||||
    EXPECT_EQ(check_sequence({0x2, 0x4, 0x2, 0xE, 0x2}), OS_LINUX);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(OsDetectionTest, TestChibiosNintendoSwitch) {
 | 
			
		||||
    EXPECT_EQ(check_sequence({0x82, 0xFF, 0x40, 0x40, 0xFF, 0x40, 0x40, 0xFF, 0x40, 0x40, 0xFF, 0x40, 0x40, 0xFF, 0x40, 0x40}), OS_LINUX);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(OsDetectionTest, TestLufaNintendoSwitch) {
 | 
			
		||||
    EXPECT_EQ(check_sequence({0x82, 0xFF, 0x40, 0x40, 0xFF, 0x40, 0x40}), OS_LINUX);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(OsDetectionTest, TestVusbNintendoSwitch) {
 | 
			
		||||
    EXPECT_EQ(check_sequence({0x82, 0xFF, 0x40, 0x40}), OS_LINUX);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(OsDetectionTest, TestChibiosQuest2) {
 | 
			
		||||
    EXPECT_EQ(check_sequence({0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF}), OS_LINUX);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(OsDetectionTest, TestVusbQuest2) {
 | 
			
		||||
    EXPECT_EQ(check_sequence({0xFF, 0xFF, 0xFF, 0xFE}), OS_LINUX);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										5
									
								
								quantum/os_detection/tests/rules.mk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								quantum/os_detection/tests/rules.mk
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
os_detection_DEFS := -DOS_DETECTION_ENABLE
 | 
			
		||||
 | 
			
		||||
os_detection_SRC := \
 | 
			
		||||
    $(QUANTUM_PATH)/os_detection/tests/os_detection.cpp \
 | 
			
		||||
    $(QUANTUM_PATH)/os_detection.c
 | 
			
		||||
							
								
								
									
										1
									
								
								quantum/os_detection/tests/testlist.mk
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								quantum/os_detection/tests/testlist.mk
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
TEST_LIST += os_detection
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue