mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2025-12-14 02:00:32 +08:00
rp2040 hid
This commit is contained in:
parent
1a8f98a64f
commit
c96057772c
@ -8,8 +8,14 @@
|
|||||||
/testenv/.tox/
|
/testenv/.tox/
|
||||||
/testenv/.mypy_cache/
|
/testenv/.mypy_cache/
|
||||||
/testenv/.ssl/
|
/testenv/.ssl/
|
||||||
/hid/.pio/
|
/hid/arduino/.pio/
|
||||||
/hid/.platformio/
|
/hid/arduino/.platformio/
|
||||||
|
/hid/pico/.pico-sdk.tmp/
|
||||||
|
/hid/pico/.pico-sdk/
|
||||||
|
/hid/pico/.tinyusb.tmp/
|
||||||
|
/hid/pico/.tinyusb/
|
||||||
|
/hid/pico/.build/
|
||||||
|
/hid/pico/*.uf2
|
||||||
/.git/
|
/.git/
|
||||||
/v*.tar.gz
|
/v*.tar.gz
|
||||||
/*.pkg.tar.xz
|
/*.pkg.tar.xz
|
||||||
|
|||||||
3
Makefile
3
Makefile
@ -213,6 +213,7 @@ keymap: testenv
|
|||||||
&& ./genmap.py keymap.csv kvmd/keyboard/mappings.py.mako kvmd/keyboard/mappings.py \
|
&& ./genmap.py keymap.csv kvmd/keyboard/mappings.py.mako kvmd/keyboard/mappings.py \
|
||||||
&& ./genmap.py keymap.csv hid/arduino/lib/drivers/usb-keymap.h.mako hid/arduino/lib/drivers/usb-keymap.h \
|
&& ./genmap.py keymap.csv hid/arduino/lib/drivers/usb-keymap.h.mako hid/arduino/lib/drivers/usb-keymap.h \
|
||||||
&& ./genmap.py keymap.csv hid/arduino/lib/drivers-avr/ps2/keymap.h.mako hid/arduino/lib/drivers-avr/ps2/keymap.h \
|
&& ./genmap.py keymap.csv hid/arduino/lib/drivers-avr/ps2/keymap.h.mako hid/arduino/lib/drivers-avr/ps2/keymap.h \
|
||||||
|
&& ./genmap.py keymap.csv hid/pico/src/ph_usb_keymap.h.mako hid/pico/src/ph_usb_keymap.h \
|
||||||
"
|
"
|
||||||
|
|
||||||
|
|
||||||
@ -251,10 +252,12 @@ clean:
|
|||||||
rm -rf testenv/run/*.{pid,sock} build site dist pkg src v*.tar.gz *.pkg.tar.{xz,zst} *.egg-info kvmd-*.tar.gz
|
rm -rf testenv/run/*.{pid,sock} build site dist pkg src v*.tar.gz *.pkg.tar.{xz,zst} *.egg-info kvmd-*.tar.gz
|
||||||
find kvmd testenv/tests -name __pycache__ | xargs rm -rf
|
find kvmd testenv/tests -name __pycache__ | xargs rm -rf
|
||||||
make -C hid/arduino clean
|
make -C hid/arduino clean
|
||||||
|
make -C hid/pico clean
|
||||||
|
|
||||||
|
|
||||||
clean-all: testenv clean
|
clean-all: testenv clean
|
||||||
make -C hid/arduino clean-all
|
make -C hid/arduino clean-all
|
||||||
|
make -C hid/pico clean-all
|
||||||
- $(DOCKER) run --rm \
|
- $(DOCKER) run --rm \
|
||||||
--volume `pwd`:/src \
|
--volume `pwd`:/src \
|
||||||
-it $(TESTENV_IMAGE) bash -c "cd src && rm -rf testenv/{.ssl,.tox,.mypy_cache,.coverage}"
|
-it $(TESTENV_IMAGE) bash -c "cd src && rm -rf testenv/{.ssl,.tox,.mypy_cache,.coverage}"
|
||||||
|
|||||||
6
hid/pico/.gitignore
vendored
Normal file
6
hid/pico/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
/.pico-sdk.tmp/
|
||||||
|
/.pico-sdk/
|
||||||
|
/.tinyusb.tmp/
|
||||||
|
/.tinyusb/
|
||||||
|
/.build/
|
||||||
|
/*.uf2
|
||||||
21
hid/pico/CMakeLists.txt
Normal file
21
hid/pico/CMakeLists.txt
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
|
||||||
|
set(PICO_SDK_PATH ${CMAKE_CURRENT_LIST_DIR}/.pico-sdk)
|
||||||
|
set(PICO_TINYUSB_PATH ${CMAKE_CURRENT_LIST_DIR}/.tinyusb)
|
||||||
|
|
||||||
|
# For TinyUSB
|
||||||
|
set(FAMILY rp2040)
|
||||||
|
|
||||||
|
# Include pico_sdk_import.cmake from pico-sdk (instead of copying)
|
||||||
|
include(${PICO_SDK_PATH}/external/pico_sdk_import.cmake)
|
||||||
|
|
||||||
|
# Generic setup
|
||||||
|
set(PROJECT hid)
|
||||||
|
project(${PROJECT})
|
||||||
|
|
||||||
|
# Initialize Pico-SDK
|
||||||
|
pico_sdk_init()
|
||||||
|
|
||||||
|
# Set the path to the source code to build
|
||||||
|
set(SRC_TO_BUILD_PATH ${CMAKE_CURRENT_LIST_DIR}/src)
|
||||||
|
add_subdirectory(${SRC_TO_BUILD_PATH})
|
||||||
36
hid/pico/Makefile
Normal file
36
hid/pico/Makefile
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
all: .pico-sdk .tinyusb
|
||||||
|
rm -f hid.uf2
|
||||||
|
cmake -B .build
|
||||||
|
cmake --build .build --config Release -- -j
|
||||||
|
ln .build/src/hid.uf2 .
|
||||||
|
|
||||||
|
|
||||||
|
upload: install
|
||||||
|
install: all
|
||||||
|
sudo mount /dev/sda1 /mnt
|
||||||
|
sudo cp hid.uf2 /mnt
|
||||||
|
sudo umount /mnt
|
||||||
|
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf .build hid.uf2
|
||||||
|
clean-all: clean
|
||||||
|
rm -rf .pico-sdk.tmp .pico-sdk .tinyusb.tmp .tinyusb
|
||||||
|
|
||||||
|
|
||||||
|
.pico-sdk:
|
||||||
|
rm -rf .pico-sdk.tmp
|
||||||
|
git clone https://github.com/raspberrypi/pico-sdk .pico-sdk.tmp
|
||||||
|
cd .pico-sdk.tmp \
|
||||||
|
&& git checkout 62201a83e2693ea165fdc7669b4ab2f3b4f43c36 \
|
||||||
|
&& git submodule update --init
|
||||||
|
mv .pico-sdk.tmp .pico-sdk
|
||||||
|
|
||||||
|
|
||||||
|
.tinyusb:
|
||||||
|
rm -rf .tinyusb.tmp
|
||||||
|
git clone https://github.com/hathach/tinyusb .tinyusb.tmp
|
||||||
|
cd .tinyusb.tmp \
|
||||||
|
&& git checkout c998e9c60bc76894006c3bd049d661124a9bfbfd \
|
||||||
|
&& git submodule update --init
|
||||||
|
mv .tinyusb.tmp .tinyusb
|
||||||
25
hid/pico/src/CMakeLists.txt
Normal file
25
hid/pico/src/CMakeLists.txt
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
set(target_name hid)
|
||||||
|
add_executable(${target_name})
|
||||||
|
|
||||||
|
target_sources(${target_name} PRIVATE
|
||||||
|
main.c
|
||||||
|
ph_outputs.c
|
||||||
|
ph_usb.c
|
||||||
|
ph_usb_kbd.c
|
||||||
|
ph_usb_mouse.c
|
||||||
|
ph_cmds.c
|
||||||
|
ph_spi.c
|
||||||
|
ph_debug.c
|
||||||
|
)
|
||||||
|
target_link_options(${target_name} PRIVATE -Xlinker --print-memory-usage)
|
||||||
|
target_compile_options(${target_name} PRIVATE -Wall -Wextra)
|
||||||
|
target_include_directories(${target_name} PRIVATE ${CMAKE_CURRENT_LIST_DIR})
|
||||||
|
|
||||||
|
target_link_libraries(${target_name} PRIVATE
|
||||||
|
pico_stdlib
|
||||||
|
pico_unique_id
|
||||||
|
hardware_spi
|
||||||
|
hardware_watchdog
|
||||||
|
tinyusb_device
|
||||||
|
)
|
||||||
|
pico_add_extra_outputs(${target_name})
|
||||||
122
hid/pico/src/main.c
Normal file
122
hid/pico/src/main.c
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
# #
|
||||||
|
# KVMD - The main PiKVM daemon. #
|
||||||
|
# #
|
||||||
|
# Copyright (C) 2018-2023 Maxim Devaev <mdevaev@gmail.com> #
|
||||||
|
# #
|
||||||
|
# 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 3 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 <https://www.gnu.org/licenses/>. #
|
||||||
|
# #
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
#include "pico/stdlib.h"
|
||||||
|
#include "hardware/gpio.h"
|
||||||
|
#include "hardware/watchdog.h"
|
||||||
|
|
||||||
|
#include "ph_types.h"
|
||||||
|
#include "ph_tools.h"
|
||||||
|
#include "ph_outputs.h"
|
||||||
|
#include "ph_usb.h"
|
||||||
|
#include "ph_spi.h"
|
||||||
|
#include "ph_proto.h"
|
||||||
|
#include "ph_cmds.h"
|
||||||
|
#include "ph_debug.h"
|
||||||
|
|
||||||
|
|
||||||
|
static bool _reset_required = false;
|
||||||
|
|
||||||
|
|
||||||
|
static u8 _handle_request(const u8 *data) { // 8 bytes
|
||||||
|
// FIXME: See kvmd/kvmd#80
|
||||||
|
// Should input buffer be cleared in this case?
|
||||||
|
if (data[0] == PH_PROTO_MAGIC && ph_crc16(data, 6) == ph_merge8_u16(data[6], data[7])) {
|
||||||
|
# define HANDLE(x_handler, x_reset) { \
|
||||||
|
x_handler(data + 2); \
|
||||||
|
if (x_reset) { _reset_required = true; } \
|
||||||
|
return PH_PROTO_PONG_OK; \
|
||||||
|
}
|
||||||
|
switch (data[1]) {
|
||||||
|
case PH_PROTO_CMD_PING: return PH_PROTO_PONG_OK;
|
||||||
|
case PH_PROTO_CMD_SET_KBD: HANDLE(ph_cmd_set_kbd, true);
|
||||||
|
case PH_PROTO_CMD_SET_MOUSE: HANDLE(ph_cmd_set_mouse, true);
|
||||||
|
case PH_PROTO_CMD_SET_CONNECTED: return PH_PROTO_PONG_OK; // Arduino AUM
|
||||||
|
case PH_PROTO_CMD_CLEAR_HID: HANDLE(ph_cmd_send_clear, false);
|
||||||
|
case PH_PROTO_CMD_KBD_KEY: HANDLE(ph_cmd_kbd_send_key, false);
|
||||||
|
case PH_PROTO_CMD_MOUSE_BUTTON: HANDLE(ph_cmd_mouse_send_button, false);
|
||||||
|
case PH_PROTO_CMD_MOUSE_ABS: HANDLE(ph_cmd_mouse_send_abs, false);
|
||||||
|
case PH_PROTO_CMD_MOUSE_REL: HANDLE(ph_cmd_mouse_send_rel, false);
|
||||||
|
case PH_PROTO_CMD_MOUSE_WHEEL: HANDLE(ph_cmd_mouse_send_wheel, false);
|
||||||
|
case PH_PROTO_CMD_REPEAT: return 0;
|
||||||
|
}
|
||||||
|
# undef HANDLE
|
||||||
|
return PH_PROTO_RESP_INVALID_ERROR;
|
||||||
|
}
|
||||||
|
return PH_PROTO_RESP_CRC_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _send_response(u8 code) {
|
||||||
|
static u8 prev_code = PH_PROTO_RESP_NONE;
|
||||||
|
if (code == 0) {
|
||||||
|
code = prev_code; // Repeat the last code
|
||||||
|
} else {
|
||||||
|
prev_code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 resp[8] = {0};
|
||||||
|
resp[0] = PH_PROTO_MAGIC_RESP;
|
||||||
|
|
||||||
|
if (code & PH_PROTO_PONG_OK) {
|
||||||
|
resp[1] = PH_PROTO_PONG_OK;
|
||||||
|
if (_reset_required) {
|
||||||
|
resp[1] |= PH_PROTO_PONG_RESET_REQUIRED;
|
||||||
|
}
|
||||||
|
resp[2] = PH_PROTO_OUT1_DYNAMIC;
|
||||||
|
|
||||||
|
resp[1] |= ph_cmd_get_offlines();
|
||||||
|
resp[1] |= ph_cmd_kbd_get_leds();
|
||||||
|
resp[2] |= ph_g_outputs_active;
|
||||||
|
resp[3] |= ph_g_outputs_avail;
|
||||||
|
} else {
|
||||||
|
resp[1] = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
ph_split16(ph_crc16(resp, 6), &resp[6], &resp[7]);
|
||||||
|
|
||||||
|
ph_spi_write(resp);
|
||||||
|
|
||||||
|
if (_reset_required) {
|
||||||
|
watchdog_reboot(0, 0, 100); // Даем немного времени чтобы отправить ответ, а потом ребутимся
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _spi_data_handler(const u8 *data) {
|
||||||
|
_send_response(_handle_request(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
ph_debug_init(false); // No UART
|
||||||
|
ph_outputs_init();
|
||||||
|
ph_usb_init();
|
||||||
|
ph_spi_init(_spi_data_handler);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
ph_usb_task();
|
||||||
|
if (!_reset_required) {
|
||||||
|
ph_spi_task();
|
||||||
|
ph_debug_act_pulse(50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
105
hid/pico/src/ph_cmds.c
Normal file
105
hid/pico/src/ph_cmds.c
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
/* ========================================================================= #
|
||||||
|
# #
|
||||||
|
# KVMD - The main PiKVM daemon. #
|
||||||
|
# #
|
||||||
|
# Copyright (C) 2018-2023 Maxim Devaev <mdevaev@gmail.com> #
|
||||||
|
# #
|
||||||
|
# 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 3 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 <https://www.gnu.org/licenses/>. #
|
||||||
|
# #
|
||||||
|
# ========================================================================= */
|
||||||
|
|
||||||
|
|
||||||
|
#include "tusb.h"
|
||||||
|
|
||||||
|
#include "ph_types.h"
|
||||||
|
#include "ph_tools.h"
|
||||||
|
#include "ph_proto.h"
|
||||||
|
#include "ph_outputs.h"
|
||||||
|
#include "ph_usb.h"
|
||||||
|
#include "ph_usb_keymap.h"
|
||||||
|
|
||||||
|
|
||||||
|
u8 ph_cmd_kbd_get_leds(void) {
|
||||||
|
u8 retval = 0;
|
||||||
|
if (PH_O_IS_KBD_USB) {
|
||||||
|
# define GET(x_mod) ((ph_g_usb_kbd_leds & KEYBOARD_LED_##x_mod##LOCK) ? PH_PROTO_PONG_##x_mod : 0)
|
||||||
|
retval = GET(CAPS) | GET(SCROLL) | GET(NUM);
|
||||||
|
# undef GET
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 ph_cmd_get_offlines(void) {
|
||||||
|
u8 retval = 0;
|
||||||
|
if (PH_O_IS_KBD_USB) {
|
||||||
|
if (!ph_g_usb_kbd_online) {
|
||||||
|
retval |= PH_PROTO_PONG_KBD_OFFLINE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (PH_O_IS_MOUSE_USB) {
|
||||||
|
if (!ph_g_usb_mouse_online) {
|
||||||
|
retval |= PH_PROTO_PONG_MOUSE_OFFLINE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ph_cmd_set_kbd(const u8 *args) { // 1 byte
|
||||||
|
ph_outputs_write(PH_PROTO_OUT1_KBD_MASK, args[0], false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ph_cmd_set_mouse(const u8 *args) { // 1 byte
|
||||||
|
ph_outputs_write(PH_PROTO_OUT1_MOUSE_MASK, args[0], false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ph_cmd_send_clear(const u8 *args) { // 0 bytes
|
||||||
|
(void)args;
|
||||||
|
ph_usb_send_clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ph_cmd_kbd_send_key(const u8 *args) { // 2 bytes
|
||||||
|
const u8 key = ph_usb_keymap(args[0]);
|
||||||
|
if (key > 0) {
|
||||||
|
ph_usb_kbd_send_key(key, args[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ph_cmd_mouse_send_button(const u8 *args) { // 2 bytes
|
||||||
|
# define HANDLE(x_byte_n, x_button) { \
|
||||||
|
if (args[x_byte_n] & PH_PROTO_CMD_MOUSE_##x_button##_SELECT) { \
|
||||||
|
const bool m_state = !!(args[x_byte_n] & PH_PROTO_CMD_MOUSE_##x_button##_STATE); \
|
||||||
|
ph_usb_mouse_send_button(MOUSE_BUTTON_##x_button, m_state); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
HANDLE(0, LEFT);
|
||||||
|
HANDLE(0, RIGHT);
|
||||||
|
HANDLE(0, MIDDLE);
|
||||||
|
HANDLE(1, BACKWARD);
|
||||||
|
HANDLE(1, FORWARD);
|
||||||
|
# undef HANDLE
|
||||||
|
}
|
||||||
|
|
||||||
|
void ph_cmd_mouse_send_abs(const u8 *args) { // 4 bytes
|
||||||
|
ph_usb_mouse_send_abs(
|
||||||
|
ph_merge8_s16(args[0], args[1]),
|
||||||
|
ph_merge8_s16(args[2], args[3]));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ph_cmd_mouse_send_rel(const u8 *args) { // 2 bytes
|
||||||
|
ph_usb_mouse_send_rel(args[0], args[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ph_cmd_mouse_send_wheel(const u8 *args) { // 2 bytes
|
||||||
|
ph_usb_mouse_send_wheel(args[0], args[1]);
|
||||||
|
}
|
||||||
37
hid/pico/src/ph_cmds.h
Normal file
37
hid/pico/src/ph_cmds.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/* ========================================================================= #
|
||||||
|
# #
|
||||||
|
# KVMD - The main PiKVM daemon. #
|
||||||
|
# #
|
||||||
|
# Copyright (C) 2018-2023 Maxim Devaev <mdevaev@gmail.com> #
|
||||||
|
# #
|
||||||
|
# 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 3 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 <https://www.gnu.org/licenses/>. #
|
||||||
|
# #
|
||||||
|
# ========================================================================= */
|
||||||
|
|
||||||
|
|
||||||
|
#include "ph_types.h"
|
||||||
|
|
||||||
|
|
||||||
|
u8 ph_cmd_kbd_get_leds(void);
|
||||||
|
u8 ph_cmd_get_offlines(void);
|
||||||
|
|
||||||
|
void ph_cmd_set_kbd(const u8 *args);
|
||||||
|
void ph_cmd_set_mouse(const u8 *args);
|
||||||
|
|
||||||
|
void ph_cmd_send_clear(const u8 *args);
|
||||||
|
void ph_cmd_kbd_send_key(const u8 *args);
|
||||||
|
void ph_cmd_mouse_send_button(const u8 *args);
|
||||||
|
void ph_cmd_mouse_send_abs(const u8 *args);
|
||||||
|
void ph_cmd_mouse_send_rel(const u8 *args);
|
||||||
|
void ph_cmd_mouse_send_wheel(const u8 *args);
|
||||||
53
hid/pico/src/ph_debug.c
Normal file
53
hid/pico/src/ph_debug.c
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
# #
|
||||||
|
# KVMD - The main PiKVM daemon. #
|
||||||
|
# #
|
||||||
|
# Copyright (C) 2018-2023 Maxim Devaev <mdevaev@gmail.com> #
|
||||||
|
# #
|
||||||
|
# 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 3 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 <https://www.gnu.org/licenses/>. #
|
||||||
|
# #
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
#include "pico/stdlib.h"
|
||||||
|
#include "hardware/gpio.h"
|
||||||
|
|
||||||
|
#include "ph_types.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define _UART uart0
|
||||||
|
#define _SPEED 3000000
|
||||||
|
#define _RX_PIN -1 // 1 - No stdin
|
||||||
|
#define _TX_PIN 0
|
||||||
|
#define _ACT_PIN 25
|
||||||
|
|
||||||
|
|
||||||
|
void ph_debug_init(bool enable_uart) {
|
||||||
|
if (enable_uart) {
|
||||||
|
stdio_uart_init_full(_UART, _SPEED, _TX_PIN, _RX_PIN);
|
||||||
|
}
|
||||||
|
gpio_init(_ACT_PIN);
|
||||||
|
gpio_set_dir(_ACT_PIN, GPIO_OUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ph_debug_act_pulse(u64 delay_ms) {
|
||||||
|
static bool flag = false;
|
||||||
|
static u64 next_ts = 0;
|
||||||
|
const u64 now_ts = time_us_64();
|
||||||
|
if (now_ts >= next_ts) {
|
||||||
|
gpio_put(_ACT_PIN, flag);
|
||||||
|
flag = !flag;
|
||||||
|
next_ts = now_ts + (delay_ms * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
27
hid/pico/src/ph_debug.h
Normal file
27
hid/pico/src/ph_debug.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
# #
|
||||||
|
# KVMD - The main PiKVM daemon. #
|
||||||
|
# #
|
||||||
|
# Copyright (C) 2018-2023 Maxim Devaev <mdevaev@gmail.com> #
|
||||||
|
# #
|
||||||
|
# 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 3 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 <https://www.gnu.org/licenses/>. #
|
||||||
|
# #
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
#include "ph_types.h"
|
||||||
|
|
||||||
|
|
||||||
|
void ph_debug_init(bool enable_uart);
|
||||||
|
void ph_debug_act_pulse(u64 delay_ms);
|
||||||
137
hid/pico/src/ph_outputs.c
Normal file
137
hid/pico/src/ph_outputs.c
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
/* ========================================================================= #
|
||||||
|
# #
|
||||||
|
# KVMD - The main PiKVM daemon. #
|
||||||
|
# #
|
||||||
|
# Copyright (C) 2018-2023 Maxim Devaev <mdevaev@gmail.com> #
|
||||||
|
# #
|
||||||
|
# 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 3 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 <https://www.gnu.org/licenses/>. #
|
||||||
|
# #
|
||||||
|
# ========================================================================= */
|
||||||
|
|
||||||
|
|
||||||
|
#include "ph_outputs.h"
|
||||||
|
|
||||||
|
#include "hardware/gpio.h"
|
||||||
|
#include "hardware/structs/watchdog.h"
|
||||||
|
|
||||||
|
#include "ph_types.h"
|
||||||
|
#include "ph_tools.h"
|
||||||
|
#include "ph_proto.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define _PS2_ENABLED_PIN 2
|
||||||
|
#define _PS2_SET_KBD_PIN 3
|
||||||
|
#define _PS2_SET_MOUSE_PIN 4
|
||||||
|
|
||||||
|
#define _USB_DISABLED_PIN 6
|
||||||
|
#define _USB_ENABLE_W98_PIN 7
|
||||||
|
#define _USB_SET_MOUSE_REL_PIN 8
|
||||||
|
#define _USB_SET_MOUSE_W98_PIN 9
|
||||||
|
|
||||||
|
|
||||||
|
u8 ph_g_outputs_active = 0;
|
||||||
|
u8 ph_g_outputs_avail = 0;
|
||||||
|
|
||||||
|
|
||||||
|
static int _read_outputs(void);
|
||||||
|
|
||||||
|
|
||||||
|
void ph_outputs_init(void) {
|
||||||
|
# define INIT_SWITCH(x_pin) { gpio_init(x_pin); gpio_set_dir(x_pin, GPIO_IN); gpio_pull_down(x_pin); }
|
||||||
|
INIT_SWITCH(_PS2_ENABLED_PIN);
|
||||||
|
INIT_SWITCH(_PS2_SET_KBD_PIN);
|
||||||
|
INIT_SWITCH(_PS2_SET_MOUSE_PIN);
|
||||||
|
|
||||||
|
INIT_SWITCH(_USB_DISABLED_PIN);
|
||||||
|
INIT_SWITCH(_USB_ENABLE_W98_PIN);
|
||||||
|
INIT_SWITCH(_USB_SET_MOUSE_REL_PIN);
|
||||||
|
INIT_SWITCH(_USB_SET_MOUSE_W98_PIN);
|
||||||
|
# undef INIT_SWITCH
|
||||||
|
|
||||||
|
const bool o_ps2_enabled = gpio_get(_PS2_ENABLED_PIN);
|
||||||
|
const bool o_ps2_kbd = gpio_get(_PS2_SET_KBD_PIN);
|
||||||
|
const bool o_ps2_mouse = gpio_get(_PS2_SET_MOUSE_PIN);
|
||||||
|
|
||||||
|
const bool o_usb_disabled = gpio_get(_USB_DISABLED_PIN);
|
||||||
|
const bool o_usb_enabled_w98 = gpio_get(_USB_ENABLE_W98_PIN);
|
||||||
|
const bool o_usb_mouse_rel = gpio_get(_USB_SET_MOUSE_REL_PIN);
|
||||||
|
const bool o_usb_mouse_w98 = gpio_get(_USB_SET_MOUSE_W98_PIN);
|
||||||
|
|
||||||
|
int outputs = _read_outputs();
|
||||||
|
if (outputs < 0) {
|
||||||
|
outputs = 0;
|
||||||
|
|
||||||
|
if (o_ps2_enabled && o_ps2_kbd) {
|
||||||
|
outputs |= PH_PROTO_OUT1_KBD_PS2;
|
||||||
|
} else if (!o_usb_disabled) {
|
||||||
|
outputs |= PH_PROTO_OUT1_KBD_USB;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (o_ps2_enabled && o_ps2_mouse) {
|
||||||
|
outputs |= PH_PROTO_OUT1_MOUSE_PS2;
|
||||||
|
} else if (!o_usb_disabled) {
|
||||||
|
if (o_usb_enabled_w98 && o_usb_mouse_w98) {
|
||||||
|
outputs |= PH_PROTO_OUT1_MOUSE_USB_W98;
|
||||||
|
} else if (o_usb_mouse_rel) {
|
||||||
|
outputs |= PH_PROTO_OUT1_MOUSE_USB_REL;
|
||||||
|
} else {
|
||||||
|
outputs |= PH_PROTO_OUT1_MOUSE_USB_ABS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ph_outputs_write(0xFF, outputs, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!o_usb_disabled) {
|
||||||
|
ph_g_outputs_avail |= PH_PROTO_OUT2_HAS_USB;
|
||||||
|
if (o_usb_enabled_w98) {
|
||||||
|
ph_g_outputs_avail |= PH_PROTO_OUT2_HAS_USB_W98;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (o_ps2_enabled) {
|
||||||
|
ph_g_outputs_avail |= PH_PROTO_OUT2_HAS_PS2;
|
||||||
|
}
|
||||||
|
|
||||||
|
ph_g_outputs_active = outputs & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ph_outputs_write(u8 mask, u8 outputs, bool force) {
|
||||||
|
int old = 0;
|
||||||
|
if (!force) {
|
||||||
|
old = _read_outputs();
|
||||||
|
if (old < 0) {
|
||||||
|
old = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
u8 data[4] = {0};
|
||||||
|
data[0] = PH_PROTO_MAGIC;
|
||||||
|
data[1] = (old & ~mask) | outputs;
|
||||||
|
ph_split16(ph_crc16(data, 2), &data[2], &data[3]);
|
||||||
|
const u32 s0 = ((u32)data[0] << 24) | ((u32)data[1] << 16) | ((u32)data[2] << 8) | (u32)data[3];
|
||||||
|
watchdog_hw->scratch[0] = s0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _read_outputs(void) {
|
||||||
|
const u32 s0 = watchdog_hw->scratch[0];
|
||||||
|
const u8 data[4] = {
|
||||||
|
(s0 >> 24) & 0xFF,
|
||||||
|
(s0 >> 16) & 0xFF,
|
||||||
|
(s0 >> 8) & 0xFF,
|
||||||
|
s0 & 0xFF,
|
||||||
|
};
|
||||||
|
if (data[0] != PH_PROTO_MAGIC || ph_crc16(data, 2) != ph_merge8_u16(data[2], data[3])) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return data[1];
|
||||||
|
}
|
||||||
42
hid/pico/src/ph_outputs.h
Normal file
42
hid/pico/src/ph_outputs.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/* ========================================================================= #
|
||||||
|
# #
|
||||||
|
# KVMD - The main PiKVM daemon. #
|
||||||
|
# #
|
||||||
|
# Copyright (C) 2018-2023 Maxim Devaev <mdevaev@gmail.com> #
|
||||||
|
# #
|
||||||
|
# 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 3 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 <https://www.gnu.org/licenses/>. #
|
||||||
|
# #
|
||||||
|
# ========================================================================= */
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ph_types.h"
|
||||||
|
#include "ph_proto.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define PH_O_KBD(x_id) ((ph_g_outputs_active & PH_PROTO_OUT1_KBD_MASK) == PH_PROTO_OUT1_KBD_##x_id)
|
||||||
|
#define PH_O_MOUSE(x_id) ((ph_g_outputs_active & PH_PROTO_OUT1_MOUSE_MASK) == PH_PROTO_OUT1_MOUSE_##x_id)
|
||||||
|
#define PH_O_IS_KBD_USB PH_O_KBD(USB)
|
||||||
|
#define PH_O_IS_MOUSE_USB (PH_O_MOUSE(USB_ABS) || PH_O_MOUSE(USB_REL) || PH_O_MOUSE(USB_W98))
|
||||||
|
#define PH_O_IS_MOUSE_USB_ABS (PH_O_MOUSE(USB_ABS) || PH_O_MOUSE(USB_W98))
|
||||||
|
#define PH_O_IS_MOUSE_USB_REL PH_O_MOUSE(USB_REL)
|
||||||
|
|
||||||
|
|
||||||
|
extern u8 ph_g_outputs_active;
|
||||||
|
extern u8 ph_g_outputs_avail;
|
||||||
|
|
||||||
|
|
||||||
|
void ph_outputs_init(void);
|
||||||
|
void ph_outputs_write(u8 mask, u8 outputs, bool force);
|
||||||
92
hid/pico/src/ph_proto.h
Normal file
92
hid/pico/src/ph_proto.h
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
# #
|
||||||
|
# KVMD - The main PiKVM daemon. #
|
||||||
|
# #
|
||||||
|
# Copyright (C) 2018-2023 Maxim Devaev <mdevaev@gmail.com> #
|
||||||
|
# #
|
||||||
|
# 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 3 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 <https://www.gnu.org/licenses/>. #
|
||||||
|
# #
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ph_types.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define PH_PROTO_MAGIC ((u8)0x33)
|
||||||
|
#define PH_PROTO_MAGIC_RESP ((u8)0x34)
|
||||||
|
|
||||||
|
//#define PH_PROTO_RESP_OK ((u8)0x20) // Legacy
|
||||||
|
#define PH_PROTO_RESP_NONE ((u8)0x24)
|
||||||
|
#define PH_PROTO_RESP_CRC_ERROR ((u8)0x40)
|
||||||
|
#define PH_PROTO_RESP_INVALID_ERROR ((u8)0x45)
|
||||||
|
#define PH_PROTO_RESP_TIMEOUT_ERROR ((u8)0x48)
|
||||||
|
|
||||||
|
// Complex response flags
|
||||||
|
#define PH_PROTO_PONG_OK ((u8)0b10000000)
|
||||||
|
#define PH_PROTO_PONG_CAPS ((u8)0b00000001)
|
||||||
|
#define PH_PROTO_PONG_SCROLL ((u8)0b00000010)
|
||||||
|
#define PH_PROTO_PONG_NUM ((u8)0b00000100)
|
||||||
|
#define PH_PROTO_PONG_KBD_OFFLINE ((u8)0b00001000)
|
||||||
|
#define PH_PROTO_PONG_MOUSE_OFFLINE ((u8)0b00010000)
|
||||||
|
#define PH_PROTO_PONG_RESET_REQUIRED ((u8)0b01000000)
|
||||||
|
|
||||||
|
// Complex request/response flags
|
||||||
|
#define PH_PROTO_OUT1_DYNAMIC ((u8)0b10000000)
|
||||||
|
#define PH_PROTO_OUT1_KBD_MASK ((u8)0b00000111)
|
||||||
|
#define PH_PROTO_OUT1_KBD_USB ((u8)0b00000001)
|
||||||
|
#define PH_PROTO_OUT1_KBD_PS2 ((u8)0b00000011)
|
||||||
|
// +
|
||||||
|
#define PH_PROTO_OUT1_MOUSE_MASK ((u8)0b00111000)
|
||||||
|
#define PH_PROTO_OUT1_MOUSE_USB_ABS ((u8)0b00001000)
|
||||||
|
#define PH_PROTO_OUT1_MOUSE_USB_REL ((u8)0b00010000)
|
||||||
|
#define PH_PROTO_OUT1_MOUSE_PS2 ((u8)0b00011000)
|
||||||
|
#define PH_PROTO_OUT1_MOUSE_USB_W98 ((u8)0b00100000)
|
||||||
|
|
||||||
|
// Complex response
|
||||||
|
#define PH_PROTO_OUT2_CONNECTABLE ((u8)0b10000000)
|
||||||
|
#define PH_PROTO_OUT2_CONNECTED ((u8)0b01000000)
|
||||||
|
#define PH_PROTO_OUT2_HAS_USB ((u8)0b00000001)
|
||||||
|
#define PH_PROTO_OUT2_HAS_PS2 ((u8)0b00000010)
|
||||||
|
#define PH_PROTO_OUT2_HAS_USB_W98 ((u8)0b00000100)
|
||||||
|
|
||||||
|
#define PH_PROTO_CMD_PING ((u8)0x01)
|
||||||
|
#define PH_PROTO_CMD_REPEAT ((u8)0x02)
|
||||||
|
#define PH_PROTO_CMD_SET_KBD ((u8)0x03)
|
||||||
|
#define PH_PROTO_CMD_SET_MOUSE ((u8)0x04)
|
||||||
|
#define PH_PROTO_CMD_SET_CONNECTED ((u8)0x05)
|
||||||
|
#define PH_PROTO_CMD_CLEAR_HID ((u8)0x10)
|
||||||
|
// +
|
||||||
|
#define PH_PROTO_CMD_KBD_KEY ((u8)0x11)
|
||||||
|
// +
|
||||||
|
#define PH_PROTO_CMD_MOUSE_ABS ((u8)0x12)
|
||||||
|
#define PH_PROTO_CMD_MOUSE_BUTTON ((u8)0x13)
|
||||||
|
#define PH_PROTO_CMD_MOUSE_WHEEL ((u8)0x14)
|
||||||
|
#define PH_PROTO_CMD_MOUSE_REL ((u8)0x15)
|
||||||
|
// +
|
||||||
|
#define PH_PROTO_CMD_MOUSE_LEFT_SELECT ((u8)0b10000000)
|
||||||
|
#define PH_PROTO_CMD_MOUSE_LEFT_STATE ((u8)0b00001000)
|
||||||
|
// +
|
||||||
|
#define PH_PROTO_CMD_MOUSE_RIGHT_SELECT ((u8)0b01000000)
|
||||||
|
#define PH_PROTO_CMD_MOUSE_RIGHT_STATE ((u8)0b00000100)
|
||||||
|
// +
|
||||||
|
#define PH_PROTO_CMD_MOUSE_MIDDLE_SELECT ((u8)0b00100000)
|
||||||
|
#define PH_PROTO_CMD_MOUSE_MIDDLE_STATE ((u8)0b00000010)
|
||||||
|
// +
|
||||||
|
#define PH_PROTO_CMD_MOUSE_BACKWARD_SELECT ((u8)0b10000000) // Previous/Up
|
||||||
|
#define PH_PROTO_CMD_MOUSE_BACKWARD_STATE ((u8)0b00001000) // Previous/Up
|
||||||
|
// +
|
||||||
|
#define PH_PROTO_CMD_MOUSE_FORWARD_SELECT ((u8)0b01000000) // Next/Down
|
||||||
|
#define PH_PROTO_CMD_MOUSE_FORWARD_STATE ((u8)0b00000100) // Next/Down
|
||||||
120
hid/pico/src/ph_spi.c
Normal file
120
hid/pico/src/ph_spi.c
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
# #
|
||||||
|
# KVMD - The main PiKVM daemon. #
|
||||||
|
# #
|
||||||
|
# Copyright (C) 2018-2023 Maxim Devaev <mdevaev@gmail.com> #
|
||||||
|
# #
|
||||||
|
# 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 3 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 <https://www.gnu.org/licenses/>. #
|
||||||
|
# #
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
#include "ph_spi.h"
|
||||||
|
|
||||||
|
#include "hardware/gpio.h"
|
||||||
|
#include "hardware/irq.h"
|
||||||
|
#include "hardware/spi.h"
|
||||||
|
#include "hardware/regs/spi.h"
|
||||||
|
|
||||||
|
#include "ph_types.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define _BUS spi0
|
||||||
|
#define _IRQ SPI0_IRQ
|
||||||
|
#define _FREQ (2 * 1000 * 1000)
|
||||||
|
#define _CS_PIN 21
|
||||||
|
#define _RX_PIN 20
|
||||||
|
#define _TX_PIN 19
|
||||||
|
#define _CLK_PIN 18
|
||||||
|
|
||||||
|
|
||||||
|
static volatile u8 _in_buf[8] = {0};
|
||||||
|
static volatile u8 _in_index = 0;
|
||||||
|
|
||||||
|
static volatile u8 _out_buf[8] = {0};
|
||||||
|
static volatile u8 _out_index = 0;
|
||||||
|
|
||||||
|
static void (*_data_cb)(const u8 *) = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
static void _xfer_isr(void);
|
||||||
|
|
||||||
|
|
||||||
|
void ph_spi_init(void (*data_cb)(const u8 *)) {
|
||||||
|
_data_cb = data_cb;
|
||||||
|
|
||||||
|
spi_init(_BUS, _FREQ);
|
||||||
|
spi_set_slave(_BUS, true);
|
||||||
|
spi_set_format(_BUS, 8, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST);
|
||||||
|
|
||||||
|
gpio_set_function(_CS_PIN, GPIO_FUNC_SPI);
|
||||||
|
gpio_set_function(_RX_PIN, GPIO_FUNC_SPI);
|
||||||
|
gpio_set_function(_TX_PIN, GPIO_FUNC_SPI);
|
||||||
|
gpio_set_function(_CLK_PIN, GPIO_FUNC_SPI);
|
||||||
|
|
||||||
|
// https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2040/hardware_regs/include/hardware/regs/spi.h
|
||||||
|
irq_set_exclusive_handler(_IRQ, _xfer_isr);
|
||||||
|
spi_get_hw(_BUS)->imsc = SPI_SSPIMSC_RXIM_BITS | SPI_SSPIMSC_TXIM_BITS;
|
||||||
|
irq_set_enabled(_IRQ, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ph_spi_task(void) {
|
||||||
|
if (!_out_buf[0] && _in_index == 8) {
|
||||||
|
_data_cb((const u8 *)_in_buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ph_spi_write(const u8 *data) {
|
||||||
|
// Меджик в нулевом байте разрешает начать ответ
|
||||||
|
for (s8 i = 7; i >= 0; --i) {
|
||||||
|
_out_buf[i] = data[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void __isr __not_in_flash_func(_xfer_isr)(void) {
|
||||||
|
# define SR (spi_get_hw(_BUS)->sr)
|
||||||
|
# define DR (spi_get_hw(_BUS)->dr)
|
||||||
|
|
||||||
|
while (SR & SPI_SSPSR_TNF_BITS) {
|
||||||
|
if (_out_buf[0] && _out_index < 8) {
|
||||||
|
DR = (u32)_out_buf[_out_index];
|
||||||
|
++_out_index;
|
||||||
|
if (_out_index == 8) {
|
||||||
|
_out_index = 0;
|
||||||
|
_in_index = 0;
|
||||||
|
_out_buf[0] = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DR = (u32)0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (SR & SPI_SSPSR_RNE_BITS) {
|
||||||
|
static bool receiving = false;
|
||||||
|
const u8 in = DR;
|
||||||
|
if (!receiving && in != 0) {
|
||||||
|
receiving = true;
|
||||||
|
}
|
||||||
|
if (receiving && _in_index < 8) {
|
||||||
|
_in_buf[_in_index] = in;
|
||||||
|
++_in_index;
|
||||||
|
}
|
||||||
|
if (_in_index == 8) {
|
||||||
|
receiving = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# undef DR
|
||||||
|
# undef SR
|
||||||
|
}
|
||||||
30
hid/pico/src/ph_spi.h
Normal file
30
hid/pico/src/ph_spi.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
# #
|
||||||
|
# KVMD - The main PiKVM daemon. #
|
||||||
|
# #
|
||||||
|
# Copyright (C) 2018-2023 Maxim Devaev <mdevaev@gmail.com> #
|
||||||
|
# #
|
||||||
|
# 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 3 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 <https://www.gnu.org/licenses/>. #
|
||||||
|
# #
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ph_types.h"
|
||||||
|
|
||||||
|
|
||||||
|
void ph_spi_init(void (*data_cb)(const u8 *));
|
||||||
|
void ph_spi_task(void);
|
||||||
|
void ph_spi_write(const u8 *data);
|
||||||
57
hid/pico/src/ph_tools.h
Normal file
57
hid/pico/src/ph_tools.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
# #
|
||||||
|
# KVMD - The main PiKVM daemon. #
|
||||||
|
# #
|
||||||
|
# Copyright (C) 2018-2023 Maxim Devaev <mdevaev@gmail.com> #
|
||||||
|
# #
|
||||||
|
# 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 3 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 <https://www.gnu.org/licenses/>. #
|
||||||
|
# #
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ph_types.h"
|
||||||
|
|
||||||
|
|
||||||
|
inline u16 ph_crc16(const u8 *buf, uz len) {
|
||||||
|
const u16 polinom = 0xA001;
|
||||||
|
u16 crc = 0xFFFF;
|
||||||
|
|
||||||
|
for (uz byte_count = 0; byte_count < len; ++byte_count) {
|
||||||
|
crc = crc ^ buf[byte_count];
|
||||||
|
for (uz bit_count = 0; bit_count < 8; ++bit_count) {
|
||||||
|
if ((crc & 0x0001) == 0) {
|
||||||
|
crc = crc >> 1;
|
||||||
|
} else {
|
||||||
|
crc = crc >> 1;
|
||||||
|
crc = crc ^ polinom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline s16 ph_merge8_s16(u8 a, u8 b) {
|
||||||
|
return (((int)a << 8) | (int)b);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline u16 ph_merge8_u16(u8 a, u8 b) {
|
||||||
|
return (((u16)a << 8) | (u16)b);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void ph_split16(u16 from, u8 *to_a, u8 *to_b) {
|
||||||
|
*to_a = (u8)(from >> 8);
|
||||||
|
*to_b = (u8)(from & 0xFF);
|
||||||
|
}
|
||||||
38
hid/pico/src/ph_types.h
Normal file
38
hid/pico/src/ph_types.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/* ========================================================================= #
|
||||||
|
# #
|
||||||
|
# KVMD - The main PiKVM daemon. #
|
||||||
|
# #
|
||||||
|
# Copyright (C) 2018-2023 Maxim Devaev <mdevaev@gmail.com> #
|
||||||
|
# #
|
||||||
|
# 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 3 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 <https://www.gnu.org/licenses/>. #
|
||||||
|
# #
|
||||||
|
# ========================================================================= */
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
|
||||||
|
typedef int8_t s8;
|
||||||
|
typedef int16_t s16;
|
||||||
|
typedef int32_t s32;
|
||||||
|
|
||||||
|
typedef size_t uz;
|
||||||
|
typedef uint8_t u8;
|
||||||
|
typedef uint16_t u16;
|
||||||
|
typedef uint32_t u32;
|
||||||
|
typedef uint64_t u64;
|
||||||
394
hid/pico/src/ph_usb.c
Normal file
394
hid/pico/src/ph_usb.c
Normal file
@ -0,0 +1,394 @@
|
|||||||
|
/* ========================================================================= #
|
||||||
|
# #
|
||||||
|
# KVMD - The main PiKVM daemon. #
|
||||||
|
# #
|
||||||
|
# Copyright (C) 2018-2023 Maxim Devaev <mdevaev@gmail.com> #
|
||||||
|
# #
|
||||||
|
# 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 3 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 <https://www.gnu.org/licenses/>. #
|
||||||
|
# #
|
||||||
|
# ========================================================================= */
|
||||||
|
|
||||||
|
|
||||||
|
#include "ph_usb.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "pico/stdlib.h"
|
||||||
|
#include "pico/unique_id.h"
|
||||||
|
|
||||||
|
#include "tusb.h"
|
||||||
|
#if TUD_OPT_HIGH_SPEED
|
||||||
|
# error "High-Speed is not supported"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "ph_types.h"
|
||||||
|
#include "ph_outputs.h"
|
||||||
|
#include "ph_usb_kbd.h"
|
||||||
|
#include "ph_usb_mouse.h"
|
||||||
|
|
||||||
|
|
||||||
|
u8 ph_g_usb_kbd_leds = 0;
|
||||||
|
bool ph_g_usb_kbd_online = true;
|
||||||
|
bool ph_g_usb_mouse_online = true;
|
||||||
|
|
||||||
|
static int _kbd_iface = -1;
|
||||||
|
static int _mouse_iface = -1;
|
||||||
|
|
||||||
|
static u8 _kbd_mods = 0;
|
||||||
|
static u8 _kbd_keys[6] = {0};
|
||||||
|
#define _KBD_CLEAR { _kbd_mods = 0; memset(_kbd_keys, 0, 6); }
|
||||||
|
|
||||||
|
static u8 _mouse_buttons = 0;
|
||||||
|
static s16 _mouse_abs_x = 0;
|
||||||
|
static s16 _mouse_abs_y = 0;
|
||||||
|
#define _MOUSE_CLEAR { _mouse_buttons = 0; _mouse_abs_x = 0; _mouse_abs_y = 0; }
|
||||||
|
|
||||||
|
|
||||||
|
static void _kbd_sync_report(bool new);
|
||||||
|
static void _mouse_abs_send_report(s8 h, s8 v);
|
||||||
|
static void _mouse_rel_send_report(s8 x, s8 y, s8 h, s8 v);
|
||||||
|
|
||||||
|
|
||||||
|
void ph_usb_init(void) {
|
||||||
|
if (PH_O_IS_KBD_USB || PH_O_IS_MOUSE_USB) {
|
||||||
|
tud_init(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ph_usb_task(void) {
|
||||||
|
if (PH_O_IS_KBD_USB || PH_O_IS_MOUSE_USB) {
|
||||||
|
tud_task();
|
||||||
|
|
||||||
|
static u64 next_ts = 0;
|
||||||
|
const u64 now_ts = time_us_64();
|
||||||
|
if (next_ts == 0 || now_ts >= next_ts) {
|
||||||
|
# define CHECK_IFACE(x_dev) \
|
||||||
|
static u64 offline_ts = 0; \
|
||||||
|
static bool prev_online = true; \
|
||||||
|
const bool online = (tud_ready() && tud_hid_n_ready(_##x_dev##_iface)); \
|
||||||
|
bool force = false; \
|
||||||
|
if (online) { \
|
||||||
|
if (!ph_g_usb_##x_dev##_online) { \
|
||||||
|
force = true; /* Если был переход из долгого оффлайна в онлайн */ \
|
||||||
|
} \
|
||||||
|
ph_g_usb_##x_dev##_online = true; \
|
||||||
|
offline_ts = 0; \
|
||||||
|
} else if (prev_online && !online) { \
|
||||||
|
offline_ts = now_ts; /* Начинаем отсчет для долгого оффлайна */ \
|
||||||
|
} else if (!prev_online && !online && offline_ts + 50000 < now_ts) { \
|
||||||
|
ph_g_usb_##x_dev##_online = false; /* Долгий оффлайн найден */ \
|
||||||
|
} \
|
||||||
|
prev_online = online;
|
||||||
|
|
||||||
|
if (_kbd_iface >= 0) {
|
||||||
|
CHECK_IFACE(kbd);
|
||||||
|
_kbd_sync_report(force);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_mouse_iface >= 0) {
|
||||||
|
CHECK_IFACE(mouse);
|
||||||
|
(void)force;
|
||||||
|
}
|
||||||
|
|
||||||
|
# undef CHECK_IFACE
|
||||||
|
next_ts = time_us_64() + 1000; // Every 1 ms
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ph_usb_kbd_send_key(u8 key, bool state) {
|
||||||
|
if (_kbd_iface < 0) {
|
||||||
|
return; // Допускаем планирование нажатия, пока устройство не готово
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key >= HID_KEY_CONTROL_LEFT && key <= HID_KEY_GUI_RIGHT) { // 0xE0...0xE7 - Modifiers
|
||||||
|
key = 1 << (key & 0x07); // Номер означает сдвиг
|
||||||
|
if (state) {
|
||||||
|
_kbd_mods |= key;
|
||||||
|
} else {
|
||||||
|
_kbd_mods &= ~key;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else { // Regular keys
|
||||||
|
if (state) {
|
||||||
|
s8 pos = -1;
|
||||||
|
for (u8 i = 0; i < 6; ++i) {
|
||||||
|
if (_kbd_keys[i] == key) {
|
||||||
|
goto already_pressed;
|
||||||
|
} else if (_kbd_keys[i] == 0) {
|
||||||
|
pos = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_kbd_keys[pos >= 0 ? pos : 0] = key;
|
||||||
|
already_pressed:
|
||||||
|
} else {
|
||||||
|
for (u8 i = 0; i < 6; ++i) {
|
||||||
|
if (_kbd_keys[i] == key) {
|
||||||
|
_kbd_keys[i] = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_kbd_sync_report(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ph_usb_mouse_send_button(u8 button, bool state) {
|
||||||
|
if (!PH_O_IS_MOUSE_USB) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (state) {
|
||||||
|
_mouse_buttons |= button;
|
||||||
|
} else {
|
||||||
|
_mouse_buttons &= ~button;
|
||||||
|
}
|
||||||
|
if (PH_O_IS_MOUSE_USB_ABS) {
|
||||||
|
_mouse_abs_send_report(0, 0);
|
||||||
|
} else { // PH_O_IS_MOUSE_USB_REL
|
||||||
|
_mouse_rel_send_report(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ph_usb_mouse_send_abs(s16 x, s16 y) {
|
||||||
|
if (PH_O_IS_MOUSE_USB_ABS) {
|
||||||
|
_mouse_abs_x = x;
|
||||||
|
_mouse_abs_y = y;
|
||||||
|
_mouse_abs_send_report(0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ph_usb_mouse_send_rel(s8 x, s8 y) {
|
||||||
|
if (PH_O_IS_MOUSE_USB_REL) {
|
||||||
|
_mouse_rel_send_report(x, y, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ph_usb_mouse_send_wheel(s8 h, s8 v) {
|
||||||
|
if (PH_O_IS_MOUSE_USB_ABS) {
|
||||||
|
_mouse_abs_send_report(h, v);
|
||||||
|
} else { // PH_O_IS_MOUSE_USB_REL
|
||||||
|
_mouse_rel_send_report(0, 0, h, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ph_usb_send_clear(void) {
|
||||||
|
if (PH_O_IS_KBD_USB) {
|
||||||
|
_KBD_CLEAR;
|
||||||
|
_kbd_sync_report(true);
|
||||||
|
}
|
||||||
|
if (PH_O_IS_MOUSE_USB) {
|
||||||
|
_MOUSE_CLEAR;
|
||||||
|
if (PH_O_IS_MOUSE_USB_ABS) {
|
||||||
|
_mouse_abs_send_report(0, 0);
|
||||||
|
} else { // PH_O_IS_MOUSE_USB_REL
|
||||||
|
_mouse_rel_send_report(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
// RAW report senders
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
|
static void _kbd_sync_report(bool new) {
|
||||||
|
static bool sent = true;
|
||||||
|
if (_kbd_iface < 0 || !PH_O_IS_KBD_USB) {
|
||||||
|
_KBD_CLEAR;
|
||||||
|
sent = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (new) {
|
||||||
|
sent = false;
|
||||||
|
}
|
||||||
|
if (!sent) {
|
||||||
|
if (tud_suspended()) {
|
||||||
|
tud_remote_wakeup();
|
||||||
|
//_KBD_CLEAR;
|
||||||
|
//sent = true;
|
||||||
|
} else {
|
||||||
|
sent = tud_hid_n_keyboard_report(_kbd_iface, 0, _kbd_mods, _kbd_keys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define _CHECK_MOUSE(x_mode) { \
|
||||||
|
if (_mouse_iface < 0 || !PH_O_IS_MOUSE_USB_##x_mode) { _MOUSE_CLEAR; return; } \
|
||||||
|
if (tud_suspended()) { tud_remote_wakeup(); _MOUSE_CLEAR; return; } \
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void _mouse_abs_send_report(s8 h, s8 v) {
|
||||||
|
(void)h; // Horizontal scrolling is not supported due BIOS/UEFI compatibility reasons
|
||||||
|
_CHECK_MOUSE(ABS);
|
||||||
|
u16 x = ((s32)_mouse_abs_x + 32768) / 2;
|
||||||
|
u16 y = ((s32)_mouse_abs_y + 32768) / 2;
|
||||||
|
if (PH_O_MOUSE(USB_W98)) {
|
||||||
|
x <<= 1;
|
||||||
|
y <<= 1;
|
||||||
|
}
|
||||||
|
struct TU_ATTR_PACKED {
|
||||||
|
u8 buttons;
|
||||||
|
u16 x;
|
||||||
|
u16 y;
|
||||||
|
s8 v;
|
||||||
|
} report = {_mouse_buttons, x, y, v};
|
||||||
|
tud_hid_n_report(_mouse_iface, 0, &report, sizeof(report));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _mouse_rel_send_report(s8 x, s8 y, s8 h, s8 v) {
|
||||||
|
(void)h; // Horizontal scrolling is not supported due BIOS/UEFI compatibility reasons
|
||||||
|
_CHECK_MOUSE(REL);
|
||||||
|
struct TU_ATTR_PACKED {
|
||||||
|
u8 buttons;
|
||||||
|
s8 x;
|
||||||
|
s8 y;
|
||||||
|
s8 v;
|
||||||
|
} report = {_mouse_buttons, x, y, v};
|
||||||
|
tud_hid_n_report(_mouse_iface, 0, &report, sizeof(report));
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef _CHECK_MOUSE
|
||||||
|
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
// Device callbacks
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
|
u16 tud_hid_get_report_cb(u8 iface, u8 report_id, hid_report_type_t report_type, u8 *buf, u16 len) {
|
||||||
|
// Invoked when received GET_REPORT control request, return 0 == STALL
|
||||||
|
(void)iface;
|
||||||
|
(void)report_id;
|
||||||
|
(void)report_type;
|
||||||
|
(void)buf;
|
||||||
|
(void)len;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tud_hid_set_report_cb(u8 iface, u8 report_id, hid_report_type_t report_type, const u8 *buf, u16 len) {
|
||||||
|
// Invoked when received SET_REPORT control request
|
||||||
|
// or received data on OUT endpoint (ReportID=0, Type=0)
|
||||||
|
(void)report_id;
|
||||||
|
if (iface == _kbd_iface && report_type == HID_REPORT_TYPE_OUTPUT && len >= 1) {
|
||||||
|
ph_g_usb_kbd_leds = buf[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const u8 *tud_hid_descriptor_report_cb(u8 iface) {
|
||||||
|
if ((int)iface == _mouse_iface) {
|
||||||
|
if (PH_O_IS_MOUSE_USB_ABS) {
|
||||||
|
return PH_USB_MOUSE_ABS_DESC;
|
||||||
|
} else { // PH_O_IS_MOUSE_USB_REL
|
||||||
|
return PH_USB_MOUSE_REL_DESC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return PH_USB_KBD_DESC; // _kbd_iface, PH_O_IS_KBD_USB
|
||||||
|
}
|
||||||
|
|
||||||
|
const u8 *tud_descriptor_configuration_cb(u8 index) {
|
||||||
|
// Invoked when received GET CONFIGURATION DESCRIPTOR
|
||||||
|
(void)index;
|
||||||
|
|
||||||
|
static u8 desc[TUD_CONFIG_DESC_LEN + TUD_HID_DESC_LEN * 2] = {0};
|
||||||
|
static bool filled = false;
|
||||||
|
|
||||||
|
if (!filled) {
|
||||||
|
uz offset = TUD_CONFIG_DESC_LEN;
|
||||||
|
u8 iface = 0;
|
||||||
|
u8 ep = 0x81;
|
||||||
|
|
||||||
|
# define APPEND_DESC(x_proto, x_desc, x_iface_to) { \
|
||||||
|
const u8 part[] = {TUD_HID_DESCRIPTOR( \
|
||||||
|
(x_iface_to = iface), /* Interface number */ \
|
||||||
|
0, x_proto, x_desc##_LEN, /* String index, protocol, report descriptor len */ \
|
||||||
|
ep, CFG_TUD_HID_EP_BUFSIZE, 1)}; /* EP In address, size, polling interval */ \
|
||||||
|
memcpy(desc + offset, part, TUD_HID_DESC_LEN); \
|
||||||
|
offset += TUD_HID_DESC_LEN; ++iface; ++ep; \
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PH_O_IS_KBD_USB) {
|
||||||
|
APPEND_DESC(HID_ITF_PROTOCOL_KEYBOARD, PH_USB_KBD_DESC, _kbd_iface);
|
||||||
|
}
|
||||||
|
if (PH_O_IS_MOUSE_USB_ABS) {
|
||||||
|
APPEND_DESC(HID_ITF_PROTOCOL_NONE, PH_USB_MOUSE_ABS_DESC, _mouse_iface);
|
||||||
|
} else if (PH_O_IS_MOUSE_USB_REL) {
|
||||||
|
APPEND_DESC(HID_ITF_PROTOCOL_MOUSE, PH_USB_MOUSE_REL_DESC, _mouse_iface);
|
||||||
|
}
|
||||||
|
|
||||||
|
# undef APPEND_DESC
|
||||||
|
|
||||||
|
// Config number, interface count, string index, total length, attribute, power in mA
|
||||||
|
const u8 part[] = {TUD_CONFIG_DESCRIPTOR(1, iface, 0, offset, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100)};
|
||||||
|
memcpy(desc, part, TUD_CONFIG_DESC_LEN);
|
||||||
|
filled = true;
|
||||||
|
}
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u8 *tud_descriptor_device_cb(void) {
|
||||||
|
// Invoked when received GET DEVICE DESCRIPTOR
|
||||||
|
|
||||||
|
static const tusb_desc_device_t desc = {
|
||||||
|
.bLength = sizeof(tusb_desc_device_t),
|
||||||
|
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||||
|
.bcdUSB = 0x0200,
|
||||||
|
|
||||||
|
.bDeviceClass = 0,
|
||||||
|
.bDeviceSubClass = 0,
|
||||||
|
.bDeviceProtocol = 0,
|
||||||
|
|
||||||
|
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||||
|
|
||||||
|
.idVendor = 0x1209, // https://pid.codes/org/Pi-KVM
|
||||||
|
.idProduct = 0xEDA2,
|
||||||
|
.bcdDevice = 0x0100,
|
||||||
|
|
||||||
|
.iManufacturer = 1,
|
||||||
|
.iProduct = 2,
|
||||||
|
.iSerialNumber = 3,
|
||||||
|
|
||||||
|
.bNumConfigurations = 1,
|
||||||
|
};
|
||||||
|
return (const u8 *)&desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u16 *tud_descriptor_string_cb(u8 index, u16 lang_id) {
|
||||||
|
// Invoked when received GET STRING DESCRIPTOR request.
|
||||||
|
(void)lang_id;
|
||||||
|
|
||||||
|
static u16 desc_str[32];
|
||||||
|
uz desc_str_len;
|
||||||
|
|
||||||
|
if (index == 0) {
|
||||||
|
desc_str[1] = 0x0409; // Supported language is English (0x0409)
|
||||||
|
desc_str_len = 1;
|
||||||
|
} else {
|
||||||
|
char str[32];
|
||||||
|
switch (index) {
|
||||||
|
case 1: strcpy(str, "PiKVM"); break; // Manufacturer
|
||||||
|
case 2: strcpy(str, "PiKVM HID"); break; // Product
|
||||||
|
case 3: pico_get_unique_board_id_string(str, 32); break; // Serial
|
||||||
|
default: return NULL;
|
||||||
|
}
|
||||||
|
desc_str_len = strlen(str);
|
||||||
|
for (uz i = 0; i < desc_str_len; ++i) {
|
||||||
|
desc_str[i + 1] = str[i]; // Convert ASCII string into UTF-16
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// First byte is length (including header), second byte is string type
|
||||||
|
desc_str[0] = (TUSB_DESC_STRING << 8) | (2 * desc_str_len + 2);
|
||||||
|
return desc_str;
|
||||||
|
}
|
||||||
43
hid/pico/src/ph_usb.h
Normal file
43
hid/pico/src/ph_usb.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/* ========================================================================= #
|
||||||
|
# #
|
||||||
|
# KVMD - The main PiKVM daemon. #
|
||||||
|
# #
|
||||||
|
# Copyright (C) 2018-2023 Maxim Devaev <mdevaev@gmail.com> #
|
||||||
|
# #
|
||||||
|
# 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 3 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 <https://www.gnu.org/licenses/>. #
|
||||||
|
# #
|
||||||
|
# ========================================================================= */
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ph_types.h"
|
||||||
|
|
||||||
|
|
||||||
|
extern u8 ph_g_usb_kbd_leds;
|
||||||
|
extern bool ph_g_usb_kbd_online;
|
||||||
|
extern bool ph_g_usb_mouse_online;
|
||||||
|
|
||||||
|
|
||||||
|
void ph_usb_init(void);
|
||||||
|
void ph_usb_task(void);
|
||||||
|
|
||||||
|
void ph_usb_kbd_send_key(u8 key, bool state);
|
||||||
|
|
||||||
|
void ph_usb_mouse_send_button(u8 button, bool state);
|
||||||
|
void ph_usb_mouse_send_abs(s16 x, s16 y);
|
||||||
|
void ph_usb_mouse_send_rel(s8 x, s8 y);
|
||||||
|
void ph_usb_mouse_send_wheel(s8 h, s8 v);
|
||||||
|
|
||||||
|
void ph_usb_send_clear(void);
|
||||||
78
hid/pico/src/ph_usb_kbd.c
Normal file
78
hid/pico/src/ph_usb_kbd.c
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
/* ========================================================================= #
|
||||||
|
# #
|
||||||
|
# KVMD - The main PiKVM daemon. #
|
||||||
|
# #
|
||||||
|
# Copyright (C) 2018-2023 Maxim Devaev <mdevaev@gmail.com> #
|
||||||
|
# #
|
||||||
|
# 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 3 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 <https://www.gnu.org/licenses/>. #
|
||||||
|
# #
|
||||||
|
# ========================================================================= */
|
||||||
|
|
||||||
|
|
||||||
|
#include "ph_usb_kbd.h"
|
||||||
|
|
||||||
|
#include "ph_types.h"
|
||||||
|
|
||||||
|
|
||||||
|
const u8 PH_USB_KBD_DESC[] = {
|
||||||
|
// Logitech descriptor. It's very similar to https://www.kernel.org/doc/Documentation/usb/gadget_hid.txt
|
||||||
|
// Dumped using usbhid-dump; parsed using https://eleccelerator.com/usbdescreqparser
|
||||||
|
|
||||||
|
// Keyboard
|
||||||
|
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
|
||||||
|
0x09, 0x06, // USAGE (Keyboard)
|
||||||
|
0xA1, 0x01, // COLLECTION (Application)
|
||||||
|
|
||||||
|
// Modifiers
|
||||||
|
0x05, 0x07, // USAGE_PAGE (Keyboard)
|
||||||
|
0x19, 0xE0, // USAGE_MINIMUM (Keyboard LeftControl)
|
||||||
|
0x29, 0xE7, // USAGE_MAXIMUM (Keyboard Right GUI)
|
||||||
|
0x15, 0x00, // LOGICAL_MINIMUM (0)
|
||||||
|
0x25, 0x01, // LOGICAL_MAXIMUM (1)
|
||||||
|
0x75, 0x01, // REPORT_SIZE (1)
|
||||||
|
0x95, 0x08, // REPORT_COUNT (8)
|
||||||
|
0x81, 0x02, // INPUT (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||||
|
|
||||||
|
// Reserved byte
|
||||||
|
0x95, 0x01, // REPORT_COUNT (1)
|
||||||
|
0x75, 0x08, // REPORT_SIZE (8)
|
||||||
|
0x81, 0x01, // INPUT (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||||
|
|
||||||
|
// LEDs output
|
||||||
|
0x95, 0x05, // REPORT_COUNT (5)
|
||||||
|
0x75, 0x01, // REPORT_SIZE (1)
|
||||||
|
0x05, 0x08, // USAGE_PAGE (LEDs)
|
||||||
|
0x19, 0x01, // USAGE_MINIMUM (Num Lock)
|
||||||
|
0x29, 0x05, // USAGE_MAXIMUM (Kana)
|
||||||
|
0x91, 0x02, // OUTPUT (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||||
|
|
||||||
|
// Reserved 3 bits in output
|
||||||
|
0x95, 0x01, // REPORT_COUNT (1)
|
||||||
|
0x75, 0x03, // REPORT_SIZE (3)
|
||||||
|
0x91, 0x01, // OUTPUT (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||||
|
|
||||||
|
// 6 keys
|
||||||
|
0x95, 0x06, // REPORT_COUNT (6)
|
||||||
|
0x75, 0x08, // REPORT_SIZE (8)
|
||||||
|
0x15, 0x00, // LOGICAL_MINIMUM (0)
|
||||||
|
0x26, 0xFF, 0x00, // LOGICAL_MAXIMUM (0xFF)
|
||||||
|
0x05, 0x07, // USAGE_PAGE (Keyboard)
|
||||||
|
0x19, 0x00, // USAGE_MINIMUM (Reserved)
|
||||||
|
0x2A, 0xFF, 0x00, // USAGE_MAXIMUM (0xFF)
|
||||||
|
0x81, 0x00, // INPUT (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||||
|
|
||||||
|
0xC0, // END_COLLECTION
|
||||||
|
};
|
||||||
|
|
||||||
|
const uz PH_USB_KBD_DESC_LEN = sizeof(PH_USB_KBD_DESC);
|
||||||
29
hid/pico/src/ph_usb_kbd.h
Normal file
29
hid/pico/src/ph_usb_kbd.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/* ========================================================================= #
|
||||||
|
# #
|
||||||
|
# KVMD - The main PiKVM daemon. #
|
||||||
|
# #
|
||||||
|
# Copyright (C) 2018-2023 Maxim Devaev <mdevaev@gmail.com> #
|
||||||
|
# #
|
||||||
|
# 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 3 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 <https://www.gnu.org/licenses/>. #
|
||||||
|
# #
|
||||||
|
# ========================================================================= */
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ph_types.h"
|
||||||
|
|
||||||
|
|
||||||
|
extern const u8 PH_USB_KBD_DESC[];
|
||||||
|
extern const uz PH_USB_KBD_DESC_LEN;
|
||||||
143
hid/pico/src/ph_usb_keymap.h
Normal file
143
hid/pico/src/ph_usb_keymap.h
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
# #
|
||||||
|
# KVMD - The main PiKVM daemon. #
|
||||||
|
# #
|
||||||
|
# Copyright (C) 2018-2023 Maxim Devaev <mdevaev@gmail.com> #
|
||||||
|
# #
|
||||||
|
# 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 3 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 <https://www.gnu.org/licenses/>. #
|
||||||
|
# #
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ph_types.h"
|
||||||
|
|
||||||
|
|
||||||
|
u8 ph_usb_keymap(u8 key) {
|
||||||
|
switch (key) {
|
||||||
|
case 1: return 4; // KeyA
|
||||||
|
case 2: return 5; // KeyB
|
||||||
|
case 3: return 6; // KeyC
|
||||||
|
case 4: return 7; // KeyD
|
||||||
|
case 5: return 8; // KeyE
|
||||||
|
case 6: return 9; // KeyF
|
||||||
|
case 7: return 10; // KeyG
|
||||||
|
case 8: return 11; // KeyH
|
||||||
|
case 9: return 12; // KeyI
|
||||||
|
case 10: return 13; // KeyJ
|
||||||
|
case 11: return 14; // KeyK
|
||||||
|
case 12: return 15; // KeyL
|
||||||
|
case 13: return 16; // KeyM
|
||||||
|
case 14: return 17; // KeyN
|
||||||
|
case 15: return 18; // KeyO
|
||||||
|
case 16: return 19; // KeyP
|
||||||
|
case 17: return 20; // KeyQ
|
||||||
|
case 18: return 21; // KeyR
|
||||||
|
case 19: return 22; // KeyS
|
||||||
|
case 20: return 23; // KeyT
|
||||||
|
case 21: return 24; // KeyU
|
||||||
|
case 22: return 25; // KeyV
|
||||||
|
case 23: return 26; // KeyW
|
||||||
|
case 24: return 27; // KeyX
|
||||||
|
case 25: return 28; // KeyY
|
||||||
|
case 26: return 29; // KeyZ
|
||||||
|
case 27: return 30; // Digit1
|
||||||
|
case 28: return 31; // Digit2
|
||||||
|
case 29: return 32; // Digit3
|
||||||
|
case 30: return 33; // Digit4
|
||||||
|
case 31: return 34; // Digit5
|
||||||
|
case 32: return 35; // Digit6
|
||||||
|
case 33: return 36; // Digit7
|
||||||
|
case 34: return 37; // Digit8
|
||||||
|
case 35: return 38; // Digit9
|
||||||
|
case 36: return 39; // Digit0
|
||||||
|
case 37: return 40; // Enter
|
||||||
|
case 38: return 41; // Escape
|
||||||
|
case 39: return 42; // Backspace
|
||||||
|
case 40: return 43; // Tab
|
||||||
|
case 41: return 44; // Space
|
||||||
|
case 42: return 45; // Minus
|
||||||
|
case 43: return 46; // Equal
|
||||||
|
case 44: return 47; // BracketLeft
|
||||||
|
case 45: return 48; // BracketRight
|
||||||
|
case 46: return 49; // Backslash
|
||||||
|
case 47: return 51; // Semicolon
|
||||||
|
case 48: return 52; // Quote
|
||||||
|
case 49: return 53; // Backquote
|
||||||
|
case 50: return 54; // Comma
|
||||||
|
case 51: return 55; // Period
|
||||||
|
case 52: return 56; // Slash
|
||||||
|
case 53: return 57; // CapsLock
|
||||||
|
case 54: return 58; // F1
|
||||||
|
case 55: return 59; // F2
|
||||||
|
case 56: return 60; // F3
|
||||||
|
case 57: return 61; // F4
|
||||||
|
case 58: return 62; // F5
|
||||||
|
case 59: return 63; // F6
|
||||||
|
case 60: return 64; // F7
|
||||||
|
case 61: return 65; // F8
|
||||||
|
case 62: return 66; // F9
|
||||||
|
case 63: return 67; // F10
|
||||||
|
case 64: return 68; // F11
|
||||||
|
case 65: return 69; // F12
|
||||||
|
case 66: return 70; // PrintScreen
|
||||||
|
case 67: return 73; // Insert
|
||||||
|
case 68: return 74; // Home
|
||||||
|
case 69: return 75; // PageUp
|
||||||
|
case 70: return 76; // Delete
|
||||||
|
case 71: return 77; // End
|
||||||
|
case 72: return 78; // PageDown
|
||||||
|
case 73: return 79; // ArrowRight
|
||||||
|
case 74: return 80; // ArrowLeft
|
||||||
|
case 75: return 81; // ArrowDown
|
||||||
|
case 76: return 82; // ArrowUp
|
||||||
|
case 77: return 224; // ControlLeft
|
||||||
|
case 78: return 225; // ShiftLeft
|
||||||
|
case 79: return 226; // AltLeft
|
||||||
|
case 80: return 227; // MetaLeft
|
||||||
|
case 81: return 228; // ControlRight
|
||||||
|
case 82: return 229; // ShiftRight
|
||||||
|
case 83: return 230; // AltRight
|
||||||
|
case 84: return 231; // MetaRight
|
||||||
|
case 85: return 72; // Pause
|
||||||
|
case 86: return 71; // ScrollLock
|
||||||
|
case 87: return 83; // NumLock
|
||||||
|
case 88: return 101; // ContextMenu
|
||||||
|
case 89: return 84; // NumpadDivide
|
||||||
|
case 90: return 85; // NumpadMultiply
|
||||||
|
case 91: return 86; // NumpadSubtract
|
||||||
|
case 92: return 87; // NumpadAdd
|
||||||
|
case 93: return 88; // NumpadEnter
|
||||||
|
case 94: return 89; // Numpad1
|
||||||
|
case 95: return 90; // Numpad2
|
||||||
|
case 96: return 91; // Numpad3
|
||||||
|
case 97: return 92; // Numpad4
|
||||||
|
case 98: return 93; // Numpad5
|
||||||
|
case 99: return 94; // Numpad6
|
||||||
|
case 100: return 95; // Numpad7
|
||||||
|
case 101: return 96; // Numpad8
|
||||||
|
case 102: return 97; // Numpad9
|
||||||
|
case 103: return 98; // Numpad0
|
||||||
|
case 104: return 99; // NumpadDecimal
|
||||||
|
case 105: return 102; // Power
|
||||||
|
case 106: return 100; // IntlBackslash
|
||||||
|
case 107: return 137; // IntlYen
|
||||||
|
case 108: return 135; // IntlRo
|
||||||
|
case 109: return 136; // KanaMode
|
||||||
|
case 110: return 138; // Convert
|
||||||
|
case 111: return 139; // NonConvert
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
39
hid/pico/src/ph_usb_keymap.h.mako
Normal file
39
hid/pico/src/ph_usb_keymap.h.mako
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
# #
|
||||||
|
# KVMD - The main PiKVM daemon. #
|
||||||
|
# #
|
||||||
|
# Copyright (C) 2018-2023 Maxim Devaev <mdevaev@gmail.com> #
|
||||||
|
# #
|
||||||
|
# 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 3 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 <https://www.gnu.org/licenses/>. #
|
||||||
|
# #
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ph_types.h"
|
||||||
|
|
||||||
|
<%! import operator %>
|
||||||
|
u8 ph_usb_keymap(u8 key) {
|
||||||
|
switch (key) {
|
||||||
|
% for km in sorted(keymap, key=operator.attrgetter("mcu_code")):
|
||||||
|
% if km.usb_key.is_modifier:
|
||||||
|
case ${km.mcu_code}: return ${km.usb_key.arduino_modifier_code}; // ${km.web_name}
|
||||||
|
% else:
|
||||||
|
case ${km.mcu_code}: return ${km.usb_key.code}; // ${km.web_name}
|
||||||
|
% endif
|
||||||
|
% endfor
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
120
hid/pico/src/ph_usb_mouse.c
Normal file
120
hid/pico/src/ph_usb_mouse.c
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
/* ========================================================================= #
|
||||||
|
# #
|
||||||
|
# KVMD - The main PiKVM daemon. #
|
||||||
|
# #
|
||||||
|
# Copyright (C) 2018-2023 Maxim Devaev <mdevaev@gmail.com> #
|
||||||
|
# #
|
||||||
|
# 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 3 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 <https://www.gnu.org/licenses/>. #
|
||||||
|
# #
|
||||||
|
# ========================================================================= */
|
||||||
|
|
||||||
|
|
||||||
|
#include "ph_usb_mouse.h"
|
||||||
|
|
||||||
|
#include "ph_types.h"
|
||||||
|
|
||||||
|
|
||||||
|
const u8 PH_USB_MOUSE_ABS_DESC[] = {
|
||||||
|
// https://github.com/NicoHood/HID/blob/0835e6a/src/SingleReport/SingleAbsoluteMouse.cpp
|
||||||
|
// Репорт взят отсюда ^^^, но изменен диапазон значений координат перемещений.
|
||||||
|
// Автор предлагает использовать -32768...32767, но семерка почему-то не хочет работать
|
||||||
|
// с отрицательными значениями координат, как не хочет хавать 65536 и 32768.
|
||||||
|
// Так что мы ей скармливаем диапазон 0...32767, и передаем рукожопам из микрософта привет,
|
||||||
|
// потому что линуксы прекрасно работают с любыми двухбайтовыми диапазонами.
|
||||||
|
|
||||||
|
// Absolute mouse
|
||||||
|
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
|
||||||
|
0x09, 0x02, // USAGE (Mouse)
|
||||||
|
0xA1, 0x01, // COLLECTION (Application)
|
||||||
|
|
||||||
|
// Pointer and Physical are required by Apple Recovery
|
||||||
|
0x09, 0x01, // USAGE (Pointer)
|
||||||
|
0xA1, 0x00, // COLLECTION (Physical)
|
||||||
|
|
||||||
|
// 8 Buttons
|
||||||
|
0x05, 0x09, // USAGE_PAGE (Button)
|
||||||
|
0x19, 0x01, // USAGE_MINIMUM (Button 1)
|
||||||
|
0x29, 0x08, // USAGE_MAXIMUM (Button 8)
|
||||||
|
0x15, 0x00, // LOGICAL_MINIMUM (0)
|
||||||
|
0x25, 0x01, // LOGICAL_MAXIMUM (1)
|
||||||
|
0x95, 0x08, // REPORT_COUNT (8)
|
||||||
|
0x75, 0x01, // REPORT_SIZE (1)
|
||||||
|
0x81, 0x02, // INPUT (Data,Var,Abs)
|
||||||
|
|
||||||
|
// X, Y
|
||||||
|
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
|
||||||
|
0x09, 0x30, // USAGE (X)
|
||||||
|
0x09, 0x31, // USAGE (Y)
|
||||||
|
0x16, 0x00, 0x00, // LOGICAL_MINIMUM (0)
|
||||||
|
0x26, 0xFF, 0x7F, // LOGICAL_MAXIMUM (32767)
|
||||||
|
0x75, 0x10, // REPORT_SIZE (16)
|
||||||
|
0x95, 0x02, // REPORT_COUNT (2)
|
||||||
|
0x81, 0x02, // INPUT (Data,Var,Abs)
|
||||||
|
|
||||||
|
// Wheel
|
||||||
|
0x09, 0x38, // USAGE (Wheel)
|
||||||
|
0x15, 0x81, // LOGICAL_MINIMUM (-127)
|
||||||
|
0x25, 0x7F, // LOGICAL_MAXIMUM (127)
|
||||||
|
0x75, 0x08, // REPORT_SIZE (8)
|
||||||
|
0x95, 0x01, // REPORT_COUNT (1)
|
||||||
|
0x81, 0x06, // INPUT (Data,Var,Rel)
|
||||||
|
|
||||||
|
// End
|
||||||
|
0xC0, // END_COLLECTION (Physical)
|
||||||
|
0xC0, // END_COLLECTION
|
||||||
|
};
|
||||||
|
|
||||||
|
const uz PH_USB_MOUSE_ABS_DESC_LEN = sizeof(PH_USB_MOUSE_ABS_DESC);
|
||||||
|
|
||||||
|
const u8 PH_USB_MOUSE_REL_DESC[] = {
|
||||||
|
// https://github.com/NicoHood/HID/blob/0835e6a/src/SingleReport/BootMouse.cpp
|
||||||
|
|
||||||
|
// Relative mouse
|
||||||
|
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
|
||||||
|
0x09, 0x02, // USAGE (Mouse)
|
||||||
|
0xA1, 0x01, // COLLECTION (Application)
|
||||||
|
|
||||||
|
// Pointer and Physical are required by Apple Recovery
|
||||||
|
0x09, 0x01, // USAGE (Pointer)
|
||||||
|
0xA1, 0x00, // COLLECTION (Physical)
|
||||||
|
|
||||||
|
// 8 Buttons
|
||||||
|
0x05, 0x09, // USAGE_PAGE (Button)
|
||||||
|
0x19, 0x01, // USAGE_MINIMUM (Button 1)
|
||||||
|
0x29, 0x08, // USAGE_MAXIMUM (Button 8)
|
||||||
|
0x15, 0x00, // LOGICAL_MINIMUM (0)
|
||||||
|
0x25, 0x01, // LOGICAL_MAXIMUM (1)
|
||||||
|
0x95, 0x08, // REPORT_COUNT (8)
|
||||||
|
0x75, 0x01, // REPORT_SIZE (1)
|
||||||
|
0x81, 0x02, // INPUT (Data,Var,Abs)
|
||||||
|
|
||||||
|
// X, Y
|
||||||
|
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
|
||||||
|
0x09, 0x30, // USAGE (X)
|
||||||
|
0x09, 0x31, // USAGE (Y)
|
||||||
|
|
||||||
|
// Wheel
|
||||||
|
0x09, 0x38, // USAGE (Wheel)
|
||||||
|
0x15, 0x81, // LOGICAL_MINIMUM (-127)
|
||||||
|
0x25, 0x7F, // LOGICAL_MAXIMUM (127)
|
||||||
|
0x75, 0x08, // REPORT_SIZE (8)
|
||||||
|
0x95, 0x03, // REPORT_COUNT (3)
|
||||||
|
0x81, 0x06, // INPUT (Data,Var,Rel)
|
||||||
|
|
||||||
|
// End
|
||||||
|
0xC0, // END_COLLECTION (Physical)
|
||||||
|
0xC0, // END_COLLECTION
|
||||||
|
};
|
||||||
|
|
||||||
|
const uz PH_USB_MOUSE_REL_DESC_LEN = sizeof(PH_USB_MOUSE_REL_DESC);
|
||||||
32
hid/pico/src/ph_usb_mouse.h
Normal file
32
hid/pico/src/ph_usb_mouse.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/* ========================================================================= #
|
||||||
|
# #
|
||||||
|
# KVMD - The main PiKVM daemon. #
|
||||||
|
# #
|
||||||
|
# Copyright (C) 2018-2023 Maxim Devaev <mdevaev@gmail.com> #
|
||||||
|
# #
|
||||||
|
# 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 3 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 <https://www.gnu.org/licenses/>. #
|
||||||
|
# #
|
||||||
|
# ========================================================================= */
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ph_types.h"
|
||||||
|
|
||||||
|
|
||||||
|
extern const u8 PH_USB_MOUSE_ABS_DESC[];
|
||||||
|
extern const uz PH_USB_MOUSE_ABS_DESC_LEN;
|
||||||
|
|
||||||
|
extern const u8 PH_USB_MOUSE_REL_DESC[];
|
||||||
|
extern const uz PH_USB_MOUSE_REL_DESC_LEN;
|
||||||
67
hid/pico/src/tusb_config.h
Normal file
67
hid/pico/src/tusb_config.h
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/* ========================================================================= #
|
||||||
|
# #
|
||||||
|
# KVMD - The main PiKVM daemon. #
|
||||||
|
# #
|
||||||
|
# Copyright (C) 2018-2023 Maxim Devaev <mdevaev@gmail.com> #
|
||||||
|
# #
|
||||||
|
# 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 3 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 <https://www.gnu.org/licenses/>. #
|
||||||
|
# #
|
||||||
|
# ========================================================================= */
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
// Common config
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
|
//#define CFG_TUSB_DEBUG 100
|
||||||
|
|
||||||
|
#define CFG_TUSB_OS OPT_OS_PICO
|
||||||
|
|
||||||
|
// Enable device stack
|
||||||
|
#define CFG_TUD_ENABLED 1
|
||||||
|
|
||||||
|
// CFG_TUSB_DEBUG is defined by compiler in DEBUG build
|
||||||
|
//#define CFG_TUSB_DEBUG 100
|
||||||
|
|
||||||
|
// USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
|
||||||
|
// Tinyusb use follows macros to declare transferring memory so that they can be put
|
||||||
|
// into those specific section.
|
||||||
|
// - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
|
||||||
|
// - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
|
||||||
|
#ifndef CFG_TUSB_MEM_SECTION
|
||||||
|
# define CFG_TUSB_MEM_SECTION
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CFG_TUSB_MEM_ALIGN
|
||||||
|
# define CFG_TUSB_MEM_ALIGN __attribute__((aligned(4)))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
// Device config
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef CFG_TUD_ENDPOINT0_SIZE
|
||||||
|
# define CFG_TUD_ENDPOINT0_SIZE 64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define CFG_TUD_HID 2
|
||||||
|
|
||||||
|
// HID buffer size Should be sufficient to hold ID (if any) + Data
|
||||||
|
#ifndef CFG_TUD_HID_EP_BUFSIZE
|
||||||
|
# define CFG_TUD_HID_EP_BUFSIZE 16
|
||||||
|
#endif
|
||||||
@ -108,6 +108,7 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many-
|
|||||||
reset_pin: int,
|
reset_pin: int,
|
||||||
reset_inverted: bool,
|
reset_inverted: bool,
|
||||||
reset_delay: float,
|
reset_delay: float,
|
||||||
|
reset_self: bool,
|
||||||
|
|
||||||
read_retries: int,
|
read_retries: int,
|
||||||
common_retries: int,
|
common_retries: int,
|
||||||
@ -126,6 +127,7 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many-
|
|||||||
|
|
||||||
self.__phy = phy
|
self.__phy = phy
|
||||||
self.__gpio = Gpio(gpio_device_path, reset_pin, reset_inverted, reset_delay)
|
self.__gpio = Gpio(gpio_device_path, reset_pin, reset_inverted, reset_delay)
|
||||||
|
self.__reset_self = reset_self
|
||||||
|
|
||||||
self.__reset_required_event = multiprocessing.Event()
|
self.__reset_required_event = multiprocessing.Event()
|
||||||
self.__events_queue: "multiprocessing.Queue[BaseEvent]" = multiprocessing.Queue()
|
self.__events_queue: "multiprocessing.Queue[BaseEvent]" = multiprocessing.Queue()
|
||||||
@ -146,6 +148,7 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many-
|
|||||||
"reset_pin": Option(4, type=valid_gpio_pin_optional),
|
"reset_pin": Option(4, type=valid_gpio_pin_optional),
|
||||||
"reset_inverted": Option(False, type=valid_bool),
|
"reset_inverted": Option(False, type=valid_bool),
|
||||||
"reset_delay": Option(0.1, type=valid_float_f01),
|
"reset_delay": Option(0.1, type=valid_float_f01),
|
||||||
|
"reset_self": Option(False, type=valid_bool),
|
||||||
|
|
||||||
"read_retries": Option(5, type=valid_int_f1),
|
"read_retries": Option(5, type=valid_int_f1),
|
||||||
"common_retries": Option(5, type=valid_int_f1),
|
"common_retries": Option(5, type=valid_int_f1),
|
||||||
@ -418,4 +421,7 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many-
|
|||||||
reset_required = (1 if response[1] & 0b01000000 else 0)
|
reset_required = (1 if response[1] & 0b01000000 else 0)
|
||||||
self.__state_flags.update(online=1, busy=reset_required, status=status)
|
self.__state_flags.update(online=1, busy=reset_required, status=status)
|
||||||
if reset_required:
|
if reset_required:
|
||||||
self.__reset_required_event.set()
|
if self.__reset_self:
|
||||||
|
time.sleep(1) # Pico перезагружается сам вскоре после ответа
|
||||||
|
else:
|
||||||
|
self.__reset_required_event.set()
|
||||||
|
|||||||
@ -100,6 +100,7 @@ class _SpiPhy(BasePhy): # pylint: disable=too-many-instance-attributes
|
|||||||
chip: int,
|
chip: int,
|
||||||
hw_cs: bool,
|
hw_cs: bool,
|
||||||
sw_cs_pin: int,
|
sw_cs_pin: int,
|
||||||
|
sw_cs_per_byte: bool,
|
||||||
max_freq: int,
|
max_freq: int,
|
||||||
block_usec: int,
|
block_usec: int,
|
||||||
read_timeout: float,
|
read_timeout: float,
|
||||||
@ -110,6 +111,7 @@ class _SpiPhy(BasePhy): # pylint: disable=too-many-instance-attributes
|
|||||||
self.__chip = chip
|
self.__chip = chip
|
||||||
self.__hw_cs = hw_cs
|
self.__hw_cs = hw_cs
|
||||||
self.__sw_cs_pin = sw_cs_pin
|
self.__sw_cs_pin = sw_cs_pin
|
||||||
|
self.__sw_cs_per_byte = sw_cs_per_byte
|
||||||
self.__max_freq = max_freq
|
self.__max_freq = max_freq
|
||||||
self.__block_usec = block_usec
|
self.__block_usec = block_usec
|
||||||
self.__read_timeout = read_timeout
|
self.__read_timeout = read_timeout
|
||||||
@ -125,7 +127,7 @@ class _SpiPhy(BasePhy): # pylint: disable=too-many-instance-attributes
|
|||||||
spi.no_cs = (not self.__hw_cs)
|
spi.no_cs = (not self.__hw_cs)
|
||||||
spi.max_speed_hz = self.__max_freq
|
spi.max_speed_hz = self.__max_freq
|
||||||
|
|
||||||
def xfer(data: bytes) -> bytes:
|
def inner_xfer(data: bytes) -> bytes:
|
||||||
try:
|
try:
|
||||||
if sw_cs_line is not None:
|
if sw_cs_line is not None:
|
||||||
sw_cs_line.set_value(0)
|
sw_cs_line.set_value(0)
|
||||||
@ -134,6 +136,17 @@ class _SpiPhy(BasePhy): # pylint: disable=too-many-instance-attributes
|
|||||||
if sw_cs_line is not None:
|
if sw_cs_line is not None:
|
||||||
sw_cs_line.set_value(1)
|
sw_cs_line.set_value(1)
|
||||||
|
|
||||||
|
if self.__sw_cs_per_byte:
|
||||||
|
# Режим для Pico, когда CS должен взводиться для отдельных байтов
|
||||||
|
def xfer(data: bytes) -> bytes:
|
||||||
|
got: list[int] = []
|
||||||
|
for byte in data:
|
||||||
|
got.extend(inner_xfer(byte.to_bytes(1, "big")))
|
||||||
|
return bytes(got)
|
||||||
|
else:
|
||||||
|
# Режим для Arduino, когда CS взводится для целого блока данных
|
||||||
|
xfer = inner_xfer
|
||||||
|
|
||||||
yield _SpiPhyConnection(
|
yield _SpiPhyConnection(
|
||||||
xfer=xfer,
|
xfer=xfer,
|
||||||
read_timeout=self.__read_timeout,
|
read_timeout=self.__read_timeout,
|
||||||
@ -167,11 +180,12 @@ class Plugin(BaseMcuHid):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def __get_phy_options(cls) -> dict:
|
def __get_phy_options(cls) -> dict:
|
||||||
return {
|
return {
|
||||||
"bus": Option(-1, type=valid_int_f0),
|
"bus": Option(-1, type=valid_int_f0),
|
||||||
"chip": Option(-1, type=valid_int_f0),
|
"chip": Option(-1, type=valid_int_f0),
|
||||||
"hw_cs": Option(False, type=valid_bool),
|
"hw_cs": Option(False, type=valid_bool),
|
||||||
"sw_cs_pin": Option(-1, type=valid_gpio_pin_optional),
|
"sw_cs_pin": Option(-1, type=valid_gpio_pin_optional),
|
||||||
"max_freq": Option(100000, type=valid_int_f1),
|
"sw_cs_per_byte": Option(False, type=valid_bool),
|
||||||
"block_usec": Option(1, type=valid_int_f0),
|
"max_freq": Option(100000, type=valid_int_f1),
|
||||||
"read_timeout": Option(0.5, type=valid_float_f01),
|
"block_usec": Option(1, type=valid_int_f0),
|
||||||
|
"read_timeout": Option(0.5, type=valid_float_f01),
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user