├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── doc ├── hw1.jpg ├── hw2.jpg ├── hw3.jpg ├── hw4.jpg ├── hw5.jpg ├── hw6.jpg └── hw7.jpg ├── pico_sdk_import.cmake └── src ├── ps2kbd.c ├── ps2phy.pio ├── ps2pico.c ├── ps2pico.h └── tusb_config.h /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /build/ 3 | /.idea/ 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13...3.27) 2 | 3 | set(CMAKE_C_STANDARD 11) 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | set(PICO_BOARD pico CACHE STRING "Board type") 7 | #set(PICO_PLATFORM rp2350 CACHE STRING "rp2350") 8 | #set(PICO_BOARD pico2 CACHE STRING "Board type") 9 | 10 | # Pull in Raspberry Pi Pico SDK 11 | include(pico_sdk_import.cmake) 12 | 13 | if (PICO_SDK_VERSION_STRING VERSION_LESS "2.1.1") 14 | message(FATAL_ERROR "Raspberry Pi Pico SDK version 2.1.1 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}") 15 | endif() 16 | 17 | set(target_name ps2pico) 18 | set(target_version 3.0) 19 | project(${target_name} C CXX ASM) 20 | 21 | # Initialise the Raspberry Pi Pico SDK 22 | pico_sdk_init() 23 | 24 | add_executable(${target_name} src/ps2pico.c src/ps2kbd.c) 25 | 26 | # print memory usage, enable all warnings 27 | target_link_options(${target_name} PRIVATE -Xlinker --print-memory-usage) 28 | target_compile_options(${target_name} PRIVATE -Wall -Wextra) 29 | 30 | add_compile_definitions(PICO_PANIC_FUNCTION=reset) 31 | add_compile_definitions(PICO_XOSC_STARTUP_DELAY_MULTIPLIER=64) 32 | 33 | add_compile_definitions(CLOCKIN=14) 34 | add_compile_definitions(CLOCKOUT=15) 35 | add_compile_definitions(DATAOUT=16) 36 | add_compile_definitions(DATAIN=17) 37 | 38 | pico_generate_pio_header(${target_name} ${CMAKE_CURRENT_LIST_DIR}/src/ps2phy.pio) 39 | 40 | pico_set_program_name(${target_name} ${target_name}) 41 | pico_set_program_version(${target_name} ${target_version}) 42 | 43 | pico_enable_stdio_uart(${target_name} 1) 44 | pico_enable_stdio_usb(${target_name} 0) 45 | 46 | target_include_directories(${target_name} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/src) 47 | target_link_libraries(${target_name} pico_stdlib hardware_pio tinyusb_host tinyusb_board) 48 | 49 | pico_add_extra_outputs(${target_name}) 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 No0ne 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ps2pico 2 | USB keyboard to PS/2+AT or XT interface converter using a Raspberry Pi Pico 3 | 4 | |![hw1](https://raw.githubusercontent.com/No0ne/ps2pico/main/doc/hw1.jpg) |![hw2](https://raw.githubusercontent.com/No0ne/ps2pico/main/doc/hw2.jpg) |![hw3](https://raw.githubusercontent.com/No0ne/ps2pico/main/doc/hw3.jpg) |![hw4](https://raw.githubusercontent.com/No0ne/ps2pico/main/doc/hw4.jpg) | 5 | |-|-|-|-| 6 | 7 | Keyboard + Mouse variant: https://github.com/No0ne/ps2x2pico 8 | 9 | Additional excellent documentation by Ray: https://minuszerodegrees.net/keyboard/ps2pico.htm 10 | 11 | # Usage 12 | * Download `ps2pico.uf2` or `ps2pico-XT.uf2` from https://github.com/No0ne/ps2pico/releases 13 | * Copy `ps2pico.uf2` or `ps2pico-XT.uf2` to your Pi Pico by pressing BOOTSEL before pluggging in. 14 | * Afterwards connect a USB keyboard using an OTG-adapter and PS/2+AT or XT 5V to Pico VBUS. 15 | * Also works with wireless keyboards with a dedicated USB receiver. 16 | * 3.3V/5V conversion is done with two NPN transistors, two zener diodes and four resistors as shown below: 17 | ``` 18 | PS/2+AT / XT CLOCK 19 | | ____ 20 | |__________|10k |___________ GPIO 14 21 | ____ | |____| | 22 | GPIO 15 ___|2k2 |____|/ BC547 __|__ 23 | |____| |\e / \ 3V6 24 | | | 25 | ____|__GND________________|___ 26 | 27 | 28 | PS/2+AT / XT DATA 29 | | ____ 30 | |_________|10k |____________ GPIO 17 31 | ____ | |____| | 32 | GPIO 16 ___|2k2 |____|/ BC547 __|__ 33 | |____| |\e / \ 3V6 34 | | | 35 | ____|__GND________________|___ 36 | ``` 37 | ![ps2pico](https://github.com/No0ne/ps2pico/assets/716129/b0133c44-c170-40f4-a3ad-c545aee92532) 38 | 39 | # NuXTv2 40 | If you have a [NuXTv2](https://monotech.fwscart.com/NuXT_v20_-_MicroATX_Turbo_XT_-_10MHz_832K_XT-IDE_Multi-IO_SVGA/p6083514_19777986.aspx) you can build an internal version of the ps2pico-XT! Replace U10 with the pico, remove RN13 and add two 4k7 pull-up resistors as shown below: 41 | 42 | |![hw5](https://raw.githubusercontent.com/No0ne/ps2pico/main/doc/hw5.jpg) |![hw6](https://raw.githubusercontent.com/No0ne/ps2pico/main/doc/hw6.jpg) |![hw7](https://raw.githubusercontent.com/No0ne/ps2pico/main/doc/hw7.jpg) | 43 | |-|-|-| 44 | 45 | # Build 46 | ``` 47 | export PICO_SDK_PATH=/path/to/pico-sdk 48 | mkdir build 49 | cd build 50 | cmake .. 51 | make 52 | ``` 53 | 54 | # Resources 55 | * https://wiki.osdev.org/PS/2_Keyboard 56 | * https://github.com/Harvie/ps2dev/blob/master/src/ps2dev.cpp 57 | * http://www.lucadavidian.com/2017/11/15/interfacing-ps2-keyboard-to-a-microcontroller/ 58 | * https://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/translate.pdf 59 | * https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-XT-Keyboard-Protocol 60 | * https://github.com/AndersBNielsen/pcxtkbd/blob/master/XT_KEYBOARD.ino 61 | -------------------------------------------------------------------------------- /doc/hw1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/No0ne/ps2pico/45c72f816a65e9f8f0ac8357a8904d25bcb27fdb/doc/hw1.jpg -------------------------------------------------------------------------------- /doc/hw2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/No0ne/ps2pico/45c72f816a65e9f8f0ac8357a8904d25bcb27fdb/doc/hw2.jpg -------------------------------------------------------------------------------- /doc/hw3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/No0ne/ps2pico/45c72f816a65e9f8f0ac8357a8904d25bcb27fdb/doc/hw3.jpg -------------------------------------------------------------------------------- /doc/hw4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/No0ne/ps2pico/45c72f816a65e9f8f0ac8357a8904d25bcb27fdb/doc/hw4.jpg -------------------------------------------------------------------------------- /doc/hw5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/No0ne/ps2pico/45c72f816a65e9f8f0ac8357a8904d25bcb27fdb/doc/hw5.jpg -------------------------------------------------------------------------------- /doc/hw6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/No0ne/ps2pico/45c72f816a65e9f8f0ac8357a8904d25bcb27fdb/doc/hw6.jpg -------------------------------------------------------------------------------- /doc/hw7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/No0ne/ps2pico/45c72f816a65e9f8f0ac8357a8904d25bcb27fdb/doc/hw7.jpg -------------------------------------------------------------------------------- /pico_sdk_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_sdk_import.cmake 2 | 3 | # This can be dropped into an external project to help locate this SDK 4 | # It should be include()ed prior to project() 5 | 6 | # Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. 7 | # 8 | # Redistribution and use in source and binary forms, with or without modification, are permitted provided that the 9 | # following conditions are met: 10 | # 11 | # 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following 12 | # disclaimer. 13 | # 14 | # 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 15 | # disclaimer in the documentation and/or other materials provided with the distribution. 16 | # 17 | # 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products 18 | # derived from this software without specific prior written permission. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 21 | # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 25 | # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 | # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) 29 | set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) 30 | message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") 31 | endif () 32 | 33 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) 34 | set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) 35 | message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") 36 | endif () 37 | 38 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) 39 | set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) 40 | message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") 41 | endif () 42 | 43 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) 44 | set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) 45 | message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") 46 | endif () 47 | 48 | if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) 49 | set(PICO_SDK_FETCH_FROM_GIT_TAG "master") 50 | message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") 51 | endif() 52 | 53 | set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") 54 | set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") 55 | set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") 56 | set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") 57 | 58 | if (NOT PICO_SDK_PATH) 59 | if (PICO_SDK_FETCH_FROM_GIT) 60 | include(FetchContent) 61 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 62 | if (PICO_SDK_FETCH_FROM_GIT_PATH) 63 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 64 | endif () 65 | FetchContent_Declare( 66 | pico_sdk 67 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 68 | GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} 69 | ) 70 | 71 | if (NOT pico_sdk) 72 | message("Downloading Raspberry Pi Pico SDK") 73 | # GIT_SUBMODULES_RECURSE was added in 3.17 74 | if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") 75 | FetchContent_Populate( 76 | pico_sdk 77 | QUIET 78 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 79 | GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} 80 | GIT_SUBMODULES_RECURSE FALSE 81 | 82 | SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src 83 | BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build 84 | SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild 85 | ) 86 | else () 87 | FetchContent_Populate( 88 | pico_sdk 89 | QUIET 90 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 91 | GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} 92 | 93 | SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src 94 | BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build 95 | SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild 96 | ) 97 | endif () 98 | 99 | set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) 100 | endif () 101 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 102 | else () 103 | message(FATAL_ERROR 104 | "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." 105 | ) 106 | endif () 107 | endif () 108 | 109 | get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 110 | if (NOT EXISTS ${PICO_SDK_PATH}) 111 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") 112 | endif () 113 | 114 | set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) 115 | if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) 116 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") 117 | endif () 118 | 119 | set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) 120 | 121 | include(${PICO_SDK_INIT_CMAKE_FILE}) 122 | -------------------------------------------------------------------------------- /src/ps2kbd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2025 No0ne (https://github.com/No0ne) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #include "tusb.h" 27 | #include "ps2pico.h" 28 | #include "atphy.pio.h" 29 | #include "bsp/board_api.h" 30 | #include "pico/util/queue.h" 31 | 32 | bool kb_enabled = true; 33 | bool locked = false; 34 | 35 | u8 leds; 36 | u8 modifiers; 37 | u8 sent = 0; 38 | u8 packet[9]; 39 | u8 last_rx = 0; 40 | u8 last_tx = 0; 41 | u8 repeat = 0; 42 | u32 repeat_us = 91743; 43 | u16 delay_ms = 500; 44 | 45 | queue_t packets; 46 | alarm_id_t repeater; 47 | extern s8 set_led; 48 | 49 | u8 const led2ps2[] = { 0, 4, 1, 5, 2, 6, 3, 7 }; 50 | u8 const mod2ps2[] = { 0x14, 0x12, 0x11, 0x1f, 0x14, 0x59, 0x11, 0x27 }; 51 | u8 const hid2ps2[] = { 52 | 0x00, 0x00, 0xfc, 0x00, 0x1c, 0x32, 0x21, 0x23, 0x24, 0x2b, 0x34, 0x33, 0x43, 0x3b, 0x42, 0x4b, 53 | 0x3a, 0x31, 0x44, 0x4d, 0x15, 0x2d, 0x1b, 0x2c, 0x3c, 0x2a, 0x1d, 0x22, 0x35, 0x1a, 0x16, 0x1e, 54 | 0x26, 0x25, 0x2e, 0x36, 0x3d, 0x3e, 0x46, 0x45, 0x5a, 0x76, 0x66, 0x0d, 0x29, 0x4e, 0x55, 0x54, 55 | 0x5b, 0x5d, 0x5d, 0x4c, 0x52, 0x0e, 0x41, 0x49, 0x4a, 0x58, 0x05, 0x06, 0x04, 0x0c, 0x03, 0x0b, 56 | 0x83, 0x0a, 0x01, 0x09, 0x78, 0x07, 0x7c, 0x7e, 0x7e, 0x70, 0x6c, 0x7d, 0x71, 0x69, 0x7a, 0x74, 57 | 0x6b, 0x72, 0x75, 0x77, 0x4a, 0x7c, 0x7b, 0x79, 0x5a, 0x69, 0x72, 0x7a, 0x6b, 0x73, 0x74, 0x6c, 58 | 0x75, 0x7d, 0x70, 0x71, 0x61, 0x2f, 0x37, 0x0f, 0x08, 0x10, 0x18, 0x20, 0x28, 0x30, 0x38, 0x40, 59 | 0x48, 0x50, 0x57, 0x5f 60 | }; 61 | u32 const repeats[] = { 62 | 33333, 37453, 41667, 45872, 48309, 54054, 58480, 62500, 63 | 66667, 75188, 83333, 91743, 100000, 108696, 116279, 125000, 64 | 133333, 149254, 166667, 181818, 200000, 217391, 232558, 250000, 65 | 270270, 303030, 333333, 370370, 400000, 434783, 476190, 500000 66 | }; 67 | u16 const delays[] = { 250, 500, 750, 1000 }; 68 | 69 | u32 ps2_frame(u8 byte) { 70 | bool parity = 1; 71 | for(u8 i = 0; i < 8; i++) { 72 | parity = parity ^ (byte >> i & 1); 73 | } 74 | return ((1 << 10) | (parity << 9) | (byte << 1)) ^ 0x7ff; 75 | } 76 | 77 | void ps2_send(u8 len) { 78 | packet[0] = len; 79 | 80 | /*printf("TX: "); 81 | for(u8 i = 1; i <= packet[0]; i++) { 82 | printf("%02x ", packet[i]); 83 | } 84 | printf("\n");*/ 85 | 86 | board_led_write(1); 87 | queue_try_add(&packets, &packet); 88 | } 89 | 90 | void kb_set_leds(u8 byte) { 91 | if(byte > 7) byte = 0; 92 | set_led = led2ps2[byte]; 93 | } 94 | 95 | s64 blink_callback() { 96 | kb_set_leds(0); 97 | packet[1] = 0xaa; 98 | ps2_send(1); 99 | return 0; 100 | } 101 | 102 | bool key_is_e0(u8 key) { 103 | return key == HID_KEY_PRINT_SCREEN || 104 | (key >= HID_KEY_INSERT && key <= HID_KEY_ARROW_UP) || 105 | key == HID_KEY_KEYPAD_DIVIDE || 106 | key == HID_KEY_KEYPAD_ENTER || 107 | key == HID_KEY_APPLICATION || 108 | key == HID_KEY_POWER || 109 | (key >= HID_KEY_GUI_LEFT && key != HID_KEY_SHIFT_RIGHT); 110 | } 111 | 112 | s64 repeat_callback() { 113 | if(repeat) { 114 | u8 len = 0; 115 | if(key_is_e0(repeat)) packet[++len] = 0xe0; 116 | 117 | if(repeat >= HID_KEY_CONTROL_LEFT && repeat <= HID_KEY_GUI_RIGHT) { 118 | packet[++len] = mod2ps2[repeat - HID_KEY_CONTROL_LEFT]; 119 | } else { 120 | packet[++len] = hid2ps2[repeat]; 121 | } 122 | 123 | ps2_send(len); 124 | return repeat_us; 125 | } 126 | 127 | repeater = 0; 128 | return 0; 129 | } 130 | 131 | void kb_receive(u8 byte, u8 prev_byte) { 132 | switch(prev_byte) { 133 | case 0xed: // Set LEDs 134 | kb_set_leds(byte); 135 | break; 136 | 137 | case 0xf3: // Set typematic rate and delay 138 | repeat_us = repeats[byte & 0x1f]; 139 | delay_ms = delays[(byte & 0x60) >> 5]; 140 | break; 141 | 142 | default: 143 | switch(byte) { 144 | case 0xff: // Reset 145 | kb_enabled = true; 146 | repeat_us = 91743; 147 | delay_ms = 500; 148 | repeat = 0; 149 | kb_set_leds(KEYBOARD_LED_NUMLOCK | KEYBOARD_LED_CAPSLOCK | KEYBOARD_LED_SCROLLLOCK); 150 | add_alarm_in_ms(500, blink_callback, NULL, false); 151 | break; 152 | 153 | case 0xee: // Echo 154 | packet[1] = 0xee; 155 | ps2_send(1); 156 | return; 157 | 158 | case 0xf2: // Identify keyboard 159 | packet[1] = 0xfa; 160 | packet[2] = 0xab; 161 | packet[3] = 0x83; 162 | ps2_send(3); 163 | return; 164 | 165 | case 0xf4: // Enable scanning 166 | kb_enabled = true; 167 | break; 168 | 169 | case 0xf5: // Disable scanning, restore default parameters 170 | case 0xf6: // Set default parameters 171 | kb_enabled = byte == 0xf6; 172 | repeat_us = 91743; 173 | delay_ms = 500; 174 | repeat = 0; 175 | kb_set_leds(0); 176 | break; 177 | } 178 | break; 179 | } 180 | 181 | packet[1] = 0xfa; 182 | ps2_send(1); 183 | } 184 | 185 | void kb_send_key(u8 key, bool state) { 186 | if(key >= HID_KEY_CONTROL_LEFT && key <= HID_KEY_GUI_RIGHT) { 187 | if(state) { 188 | modifiers = modifiers | (1 << (key - HID_KEY_CONTROL_LEFT)); 189 | } else { 190 | modifiers = modifiers & ~(1 << (key - HID_KEY_CONTROL_LEFT)); 191 | } 192 | } else if(key < HID_KEY_A || key > HID_KEY_F24) { 193 | return; 194 | } 195 | 196 | u8 len = 0; 197 | //printf("HID code = %02x, state = %01x\n", key, state); 198 | 199 | if(!kb_enabled) { 200 | printf("kb_enabled = false\n"); 201 | return; 202 | } 203 | 204 | if(key == HID_KEY_PAUSE) { 205 | repeat = 0; 206 | 207 | if(state) { 208 | if(modifiers & KEYBOARD_MODIFIER_LEFTCTRL || 209 | modifiers & KEYBOARD_MODIFIER_RIGHTCTRL) { 210 | packet[++len] = 0xe0; 211 | packet[++len] = 0x7e; 212 | packet[++len] = 0xe0; 213 | packet[++len] = 0xf0; 214 | packet[++len] = 0x7e; 215 | } else { 216 | packet[++len] = 0xe1; 217 | packet[++len] = 0x14; 218 | packet[++len] = 0x77; 219 | packet[++len] = 0xe1; 220 | packet[++len] = 0xf0; 221 | packet[++len] = 0x14; 222 | packet[++len] = 0xf0; 223 | packet[++len] = 0x77; 224 | } 225 | 226 | ps2_send(len); 227 | } 228 | 229 | return; 230 | } 231 | 232 | if(key_is_e0(key)) packet[++len] = 0xe0; 233 | 234 | if(state) { 235 | repeat = key; 236 | if(repeater) cancel_alarm(repeater); 237 | repeater = add_alarm_in_ms(delay_ms, repeat_callback, NULL, false); 238 | } else { 239 | if(key == repeat) repeat = 0; 240 | packet[++len] = 0xf0; 241 | } 242 | 243 | if(key >= HID_KEY_CONTROL_LEFT && key <= HID_KEY_GUI_RIGHT) { 244 | packet[++len] = mod2ps2[key - HID_KEY_CONTROL_LEFT]; 245 | } else { 246 | packet[++len] = hid2ps2[key]; 247 | } 248 | 249 | ps2_send(len); 250 | } 251 | 252 | void kb_task() { 253 | if(pio_interrupt_get(pio0, 1)) { 254 | if(sent > 0) sent--; 255 | pio_interrupt_clear(pio0, 1); 256 | } 257 | 258 | if(!locked && !queue_is_empty(&packets) && !pio_interrupt_get(pio0, 0)) { 259 | if(queue_try_peek(&packets, &packet)) { 260 | if(sent == packet[0]) { 261 | sent = 0; 262 | queue_try_remove(&packets, &packet); 263 | board_led_write(0); 264 | } else { 265 | sent++; 266 | last_tx = packet[sent]; 267 | locked = true; 268 | //printf(" put %02x \n", last_tx); 269 | pio_sm_put(pio0, 0, ps2_frame(last_tx)); 270 | } 271 | } 272 | } 273 | 274 | if(locked && pio_interrupt_get(pio0, 0)) locked = false; 275 | 276 | if(!pio_sm_is_rx_fifo_empty(pio0, 1)) { 277 | u32 fifo = pio_sm_get(pio0, 1) >> 23; 278 | 279 | bool parity = 1; 280 | for(u8 i = 0; i < 8; i++) { 281 | parity = parity ^ (fifo >> i & 1); 282 | } 283 | 284 | if(parity != fifo >> 8) { 285 | pio_sm_put(pio0, 0, ps2_frame(0xfe)); 286 | return; 287 | } 288 | 289 | if((fifo & 0xff) == 0xfe) { 290 | pio_sm_put(pio0, 0, ps2_frame(last_tx)); 291 | return; 292 | } 293 | 294 | while(queue_try_remove(&packets, &packet)); 295 | sent = 0; 296 | 297 | //printf("RX: 0x%02lx\n", fifo & 0xff); 298 | 299 | kb_receive(fifo, last_rx); 300 | last_rx = fifo; 301 | } 302 | } 303 | 304 | void kb_init() { 305 | ps2write_program_init(pio0, 0, pio_add_program(pio0, &ps2write_program)); 306 | ps2read_program_init(pio0, 1, pio_add_program(pio0, &ps2read_program)); 307 | queue_init(&packets, 9, 32); 308 | } 309 | -------------------------------------------------------------------------------- /src/ps2phy.pio: -------------------------------------------------------------------------------- 1 | ; 2 | ; Copyright (c) 2025 No0ne (https://github.com/No0ne) 3 | ; 4 | ; SPDX-License-Identifier: MIT 5 | ; 6 | 7 | .program ps2write 8 | 9 | restart: 10 | set pins, 0 // clock high 11 | irq clear 0 // clear busy flag 12 | pull ifempty block // move new byte into OSR 13 | irq nowait 0 // set busy flag 14 | set x, 10 // number of bits to write out 15 | 16 | loop: 17 | set pins, 0 // clock high 18 | jmp pin, write // if clock is high, host is still receiving data 19 | out null, 32 // clock was low, clear OSR 20 | irq wait 1 // host wants to send data, notify of failure to send data 21 | jmp restart // and wait for restart 22 | 23 | write: 24 | out pins, 1 // write out data 25 | set pins, 1 // set clock low 26 | jmp x--, loop // iterate 27 | 28 | 29 | .program ps2read 30 | 31 | restart: 32 | set pins, 0 // set clock and data high 33 | wait 1 gpio, 14 // CLOCKIN=14 // wait for clock to be pulled high 34 | wait 0 irq, 0 // wait for busy flag to get cleared 35 | jmp pin, restart // restart if not receiving 36 | set pins, 1 // clock low 37 | set x, 8 // set loop counter 38 | 39 | loop: 40 | set pins, 0 // clock high 41 | in pins, 1 // read a bit into ISR 42 | set pins, 1 // clock low 43 | jmp x--, loop // iterate 44 | set pins, 0 // clock high 45 | nop // cycle delay 46 | set pins, 1 // data high, clock low 47 | set pins, 3 // data low, clock low 48 | nop // cycle delay 49 | set pins, 2 // data low, clock high 50 | nop // cycle delay 51 | 52 | % c-sdk { 53 | 54 | void ps2write_program_init(PIO pio, uint sm, uint offset) { 55 | pio_sm_config c = ps2write_program_get_default_config(offset); 56 | 57 | pio_gpio_init(pio, CLOCKOUT); 58 | pio_gpio_init(pio, DATAOUT); 59 | 60 | sm_config_set_clkdiv(&c, 1500); 61 | sm_config_set_jmp_pin(&c, CLOCKIN); 62 | sm_config_set_set_pins(&c, CLOCKOUT, 1); 63 | sm_config_set_out_pins(&c, DATAOUT, 1); 64 | sm_config_set_out_shift(&c, true, false, 11); 65 | 66 | pio_sm_set_consecutive_pindirs(pio, sm, CLOCKOUT, 2, true); 67 | pio_sm_init(pio, sm, offset, &c); 68 | pio_sm_set_enabled(pio, sm, true); 69 | } 70 | 71 | void ps2read_program_init(PIO pio, uint sm, uint offset) { 72 | pio_sm_config c = ps2read_program_get_default_config(offset); 73 | 74 | sm_config_set_clkdiv(&c, 1875); 75 | sm_config_set_jmp_pin(&c, DATAIN); 76 | sm_config_set_set_pins(&c, CLOCKOUT, 2); 77 | sm_config_set_in_pins(&c, DATAIN); 78 | sm_config_set_in_shift(&c, true, true, 9); 79 | 80 | pio_sm_init(pio, sm, offset, &c); 81 | pio_sm_set_enabled(pio, sm, true); 82 | } 83 | 84 | %} 85 | -------------------------------------------------------------------------------- /src/ps2pico.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2025 No0ne (https://github.com/No0ne) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #include "tusb.h" 27 | #include "ps2pico.h" 28 | #include "bsp/board_api.h" 29 | #include "hardware/clocks.h" 30 | #include "hardware/watchdog.h" 31 | 32 | struct { 33 | u8 report_count; 34 | tuh_hid_report_info_t report_info[MAX_REPORT]; 35 | u8 dev_addr; 36 | u8 modifiers; 37 | u8 boot[MAX_BOOT]; 38 | u8 nkro[MAX_NKRO]; 39 | } keyboards[CFG_TUH_HID]; 40 | 41 | void tuh_hid_mount_cb(u8 dev_addr, u8 instance, u8 const* desc_report, u16 desc_len) { 42 | if(tuh_hid_interface_protocol(dev_addr, instance) == HID_ITF_PROTOCOL_MOUSE) return; 43 | 44 | keyboards[instance].report_count = tuh_hid_parse_report_descriptor(keyboards[instance].report_info, MAX_REPORT, desc_report, desc_len); 45 | printf("HID device address = %d, instance = %d is mounted with %u reports, ", dev_addr, instance, keyboards[instance].report_count); 46 | 47 | if(!tuh_hid_receive_report(dev_addr, instance)) { 48 | printf("ERROR: Could not register for HID(%d,%d)!\n", dev_addr, instance); 49 | } else { 50 | printf("HID(%d,%d) registered for reports\n", dev_addr, instance); 51 | keyboards[instance].dev_addr = dev_addr; 52 | keyboards[instance].modifiers = 0; 53 | memset(keyboards[instance].boot, 0, MAX_BOOT); 54 | memset(keyboards[instance].nkro, 0, MAX_NKRO); 55 | board_led_write(0); 56 | } 57 | } 58 | 59 | void tuh_hid_umount_cb(u8 dev_addr, u8 instance) { 60 | printf("HID device address = %d, instance = %d is unmounted\n", dev_addr, instance); 61 | keyboards[instance].dev_addr = 0; 62 | } 63 | 64 | void tuh_hid_report_received_cb(u8 dev_addr, u8 instance, u8 const* report, u16 len) { 65 | u8 const rpt_count = keyboards[instance].report_count; 66 | tuh_hid_report_info_t *rpt_infos = keyboards[instance].report_info; 67 | tuh_hid_report_info_t *rpt_info = NULL; 68 | 69 | if(rpt_count == 1 && rpt_infos[0].report_id == 0) { 70 | rpt_info = &rpt_infos[0]; 71 | } else { 72 | u8 const rpt_id = report[0]; 73 | for(u8 i = 0; i < rpt_count; i++) { 74 | if(rpt_id == rpt_infos[i].report_id) { 75 | rpt_info = &rpt_infos[i]; 76 | break; 77 | } 78 | } 79 | report++; 80 | len--; 81 | } 82 | 83 | if(!rpt_info) return; 84 | board_led_write(1); 85 | tuh_hid_receive_report(dev_addr, instance); 86 | 87 | if(rpt_info->usage_page == HID_USAGE_PAGE_CONSUMER && rpt_info->usage == HID_USAGE_CONSUMER_CONTROL) { 88 | printf("len %d %02x %02x %02x %02x\n", len, report[0], report[1], report[2], report[3]); 89 | return; 90 | } 91 | 92 | if(rpt_info->usage_page != HID_USAGE_PAGE_DESKTOP || rpt_info->usage != HID_USAGE_DESKTOP_KEYBOARD) { 93 | printf("UNKNOWN key usage_page: %02x usage: %02x\n", rpt_info->usage_page, rpt_info->usage); 94 | return; 95 | } 96 | 97 | if(report[0] != keyboards[instance].modifiers) { 98 | for(u8 i = 0; i < 8; i++) { 99 | if((report[0] >> i & 1) != (keyboards[instance].modifiers >> i & 1)) { 100 | kb_send_key(i + HID_KEY_CONTROL_LEFT, report[0] >> i & 1); 101 | } 102 | } 103 | 104 | keyboards[instance].modifiers = report[0]; 105 | } 106 | 107 | report++; 108 | len--; 109 | 110 | if(len > 12 && len < 31) { 111 | for(u8 i = 0; i < len && i < MAX_NKRO; i++) { 112 | for(u8 j = 0; j < 8; j++) { 113 | if((report[i] >> j & 1) != (keyboards[instance].nkro[i] >> j & 1)) { 114 | kb_send_key(i*8+j, report[i] >> j & 1); 115 | } 116 | } 117 | } 118 | 119 | memcpy(keyboards[instance].nkro, report, len > MAX_NKRO ? MAX_NKRO : len); 120 | return; 121 | } 122 | 123 | switch(len) { 124 | case 8: 125 | case 7: 126 | report++; 127 | // fall through 128 | case 6: 129 | for(u8 i = 0; i < MAX_BOOT; i++) { 130 | if(keyboards[instance].boot[i]) { 131 | bool brk = true; 132 | 133 | for(u8 j = 0; j < MAX_BOOT; j++) { 134 | if(keyboards[instance].boot[i] == report[j]) { 135 | brk = false; 136 | break; 137 | } 138 | } 139 | 140 | if(brk) kb_send_key(keyboards[instance].boot[i], false); 141 | } 142 | } 143 | 144 | for(u8 i = 0; i < MAX_BOOT; i++) { 145 | if(report[i]) { 146 | bool make = true; 147 | 148 | for(u8 j = 0; j < MAX_BOOT; j++) { 149 | if(report[i] == keyboards[instance].boot[j]) { 150 | make = false; 151 | break; 152 | } 153 | } 154 | 155 | if(make) kb_send_key(report[i], true); 156 | } 157 | } 158 | 159 | memcpy(keyboards[instance].boot, report, MAX_BOOT); 160 | return; 161 | } 162 | 163 | printf("UKNOWN keyboard len: %d\n", len); 164 | } 165 | 166 | s8 set_led = -1; 167 | u8 inst_loop = 0; 168 | u8 last_dev = 0; 169 | 170 | s64 set_led_callback() { 171 | if(set_led == -1) return 50000; 172 | 173 | if(keyboards[inst_loop].dev_addr && last_dev != keyboards[inst_loop].dev_addr) { 174 | tuh_hid_set_report(keyboards[inst_loop].dev_addr, inst_loop, 0, HID_REPORT_TYPE_OUTPUT, &set_led, 1); 175 | last_dev = keyboards[inst_loop].dev_addr; 176 | } 177 | 178 | inst_loop++; 179 | 180 | if(inst_loop == CFG_TUH_HID) { 181 | inst_loop = 0; 182 | set_led = -1; 183 | return 100000; 184 | } 185 | 186 | return 1000; 187 | } 188 | 189 | int main() { 190 | set_sys_clock_khz(120000, true); 191 | 192 | board_init(); 193 | board_led_write(1); 194 | 195 | printf("\n%s-%s ", PICO_PROGRAM_NAME, PICO_PROGRAM_VERSION_STRING); 196 | printf("PS/2+AT version\n"); 197 | 198 | tuh_hid_set_default_protocol(HID_PROTOCOL_REPORT); 199 | tuh_init(BOARD_TUH_RHPORT); 200 | 201 | kb_init(); 202 | add_alarm_in_ms(500, set_led_callback, NULL, false); 203 | 204 | while(1) { 205 | tuh_task(); 206 | kb_task(); 207 | } 208 | } 209 | 210 | void reset() { 211 | printf("\n\n *** PANIC via tinyusb: watchdog reset!\n\n"); 212 | watchdog_enable(100, false); 213 | } 214 | -------------------------------------------------------------------------------- /src/ps2pico.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2024 No0ne (https://github.com/No0ne) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | typedef int8_t s8; 32 | typedef int16_t s16; 33 | typedef int32_t s32; 34 | typedef int64_t s64; 35 | 36 | typedef uint8_t u8; 37 | typedef uint16_t u16; 38 | typedef uint32_t u32; 39 | typedef uint64_t u64; 40 | 41 | #define MAX_BOOT 6 42 | #define MAX_NKRO 16 43 | #define MAX_REPORT 8 44 | 45 | void kb_init(); 46 | void kb_task(); 47 | void kb_send_key(u8 key, bool state); -------------------------------------------------------------------------------- /src/tusb_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 Ha Thach (tinyusb.org) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #ifndef _TUSB_CONFIG_H_ 27 | #define _TUSB_CONFIG_H_ 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | //-------------------------------------------------------------------- 34 | // Common Configuration 35 | //-------------------------------------------------------------------- 36 | 37 | #define CFG_TUSB_MCU OPT_MCU_RP2040 38 | #define CFG_TUSB_OS OPT_OS_PICO 39 | #define CFG_TUSB_RHPORT0_MODE OPT_MODE_HOST 40 | 41 | #ifndef CFG_TUSB_DEBUG 42 | #define CFG_TUSB_DEBUG 0 43 | #endif 44 | 45 | /* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. 46 | * Tinyusb use follows macros to declare transferring memory so that they can be put 47 | * into those specific section. 48 | * e.g 49 | * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) 50 | * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) 51 | */ 52 | #ifndef CFG_TUH_MEM_SECTION 53 | #define CFG_TUH_MEM_SECTION 54 | #endif 55 | 56 | #ifndef CFG_TUH_MEM_ALIGN 57 | #define CFG_TUH_MEM_ALIGN __attribute__ ((aligned(4))) 58 | #endif 59 | 60 | //-------------------------------------------------------------------- 61 | // Host Configuration 62 | //-------------------------------------------------------------------- 63 | 64 | // Enable Host stack 65 | #define CFG_TUH_ENABLED 1 66 | 67 | #if CFG_TUSB_MCU == OPT_MCU_RP2040 68 | // #define CFG_TUH_RPI_PIO_USB 1 // use pio-usb as host controller 69 | // #define CFG_TUH_MAX3421 1 // use max3421 as host controller 70 | 71 | // host roothub port is 1 if using either pio-usb or max3421 72 | #if (defined(CFG_TUH_RPI_PIO_USB) && CFG_TUH_RPI_PIO_USB) || (defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421) 73 | #define BOARD_TUH_RHPORT 1 74 | #endif 75 | #endif 76 | 77 | // Default is max speed that hardware controller could support with on-chip PHY 78 | #define CFG_TUH_MAX_SPEED BOARD_TUH_MAX_SPEED 79 | 80 | //------------------------- Board Specific -------------------------- 81 | 82 | // RHPort number used for host can be defined by board.mk, default to port 0 83 | #ifndef BOARD_TUH_RHPORT 84 | #define BOARD_TUH_RHPORT 0 85 | #endif 86 | 87 | // RHPort max operational speed can defined by board.mk 88 | #ifndef BOARD_TUH_MAX_SPEED 89 | #define BOARD_TUH_MAX_SPEED OPT_MODE_DEFAULT_SPEED 90 | #endif 91 | 92 | //-------------------------------------------------------------------- 93 | // Driver Configuration 94 | //-------------------------------------------------------------------- 95 | 96 | // Size of buffer to hold descriptors and other data used for enumeration 97 | #define CFG_TUH_ENUMERATION_BUFSIZE 256 98 | 99 | #define CFG_TUH_HUB 2 // number of supported hubs 100 | 101 | // max device support (excluding hub device): 1 hub typically has 4 ports 102 | #define CFG_TUH_DEVICE_MAX (3*CFG_TUH_HUB + 1) 103 | 104 | #define CFG_TUH_HID (3*CFG_TUH_DEVICE_MAX) // typical keyboard + mouse device can have 3-4 HID interfaces 105 | 106 | //------------- HID -------------// 107 | #define CFG_TUH_HID_EPIN_BUFSIZE 64 108 | #define CFG_TUH_HID_EPOUT_BUFSIZE 64 109 | 110 | #ifdef __cplusplus 111 | } 112 | #endif 113 | 114 | #endif /* _TUSB_CONFIG_H_ */ 115 | --------------------------------------------------------------------------------