Skip to content

Commit c960577

Browse files
committed
rp2040 hid
1 parent 1a8f98a commit c960577

29 files changed

+1933
-11
lines changed

.dockerignore

+8-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,14 @@
88
/testenv/.tox/
99
/testenv/.mypy_cache/
1010
/testenv/.ssl/
11-
/hid/.pio/
12-
/hid/.platformio/
11+
/hid/arduino/.pio/
12+
/hid/arduino/.platformio/
13+
/hid/pico/.pico-sdk.tmp/
14+
/hid/pico/.pico-sdk/
15+
/hid/pico/.tinyusb.tmp/
16+
/hid/pico/.tinyusb/
17+
/hid/pico/.build/
18+
/hid/pico/*.uf2
1319
/.git/
1420
/v*.tar.gz
1521
/*.pkg.tar.xz

Makefile

+3
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ keymap: testenv
213213
&& ./genmap.py keymap.csv kvmd/keyboard/mappings.py.mako kvmd/keyboard/mappings.py \
214214
&& ./genmap.py keymap.csv hid/arduino/lib/drivers/usb-keymap.h.mako hid/arduino/lib/drivers/usb-keymap.h \
215215
&& ./genmap.py keymap.csv hid/arduino/lib/drivers-avr/ps2/keymap.h.mako hid/arduino/lib/drivers-avr/ps2/keymap.h \
216+
&& ./genmap.py keymap.csv hid/pico/src/ph_usb_keymap.h.mako hid/pico/src/ph_usb_keymap.h \
216217
"
217218

218219

@@ -251,10 +252,12 @@ clean:
251252
rm -rf testenv/run/*.{pid,sock} build site dist pkg src v*.tar.gz *.pkg.tar.{xz,zst} *.egg-info kvmd-*.tar.gz
252253
find kvmd testenv/tests -name __pycache__ | xargs rm -rf
253254
make -C hid/arduino clean
255+
make -C hid/pico clean
254256

255257

256258
clean-all: testenv clean
257259
make -C hid/arduino clean-all
260+
make -C hid/pico clean-all
258261
- $(DOCKER) run --rm \
259262
--volume `pwd`:/src \
260263
-it $(TESTENV_IMAGE) bash -c "cd src && rm -rf testenv/{.ssl,.tox,.mypy_cache,.coverage}"

hid/pico/.gitignore

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/.pico-sdk.tmp/
2+
/.pico-sdk/
3+
/.tinyusb.tmp/
4+
/.tinyusb/
5+
/.build/
6+
/*.uf2

hid/pico/CMakeLists.txt

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
cmake_minimum_required(VERSION 3.13)
2+
3+
set(PICO_SDK_PATH ${CMAKE_CURRENT_LIST_DIR}/.pico-sdk)
4+
set(PICO_TINYUSB_PATH ${CMAKE_CURRENT_LIST_DIR}/.tinyusb)
5+
6+
# For TinyUSB
7+
set(FAMILY rp2040)
8+
9+
# Include pico_sdk_import.cmake from pico-sdk (instead of copying)
10+
include(${PICO_SDK_PATH}/external/pico_sdk_import.cmake)
11+
12+
# Generic setup
13+
set(PROJECT hid)
14+
project(${PROJECT})
15+
16+
# Initialize Pico-SDK
17+
pico_sdk_init()
18+
19+
# Set the path to the source code to build
20+
set(SRC_TO_BUILD_PATH ${CMAKE_CURRENT_LIST_DIR}/src)
21+
add_subdirectory(${SRC_TO_BUILD_PATH})

hid/pico/Makefile

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
all: .pico-sdk .tinyusb
2+
rm -f hid.uf2
3+
cmake -B .build
4+
cmake --build .build --config Release -- -j
5+
ln .build/src/hid.uf2 .
6+
7+
8+
upload: install
9+
install: all
10+
sudo mount /dev/sda1 /mnt
11+
sudo cp hid.uf2 /mnt
12+
sudo umount /mnt
13+
14+
15+
clean:
16+
rm -rf .build hid.uf2
17+
clean-all: clean
18+
rm -rf .pico-sdk.tmp .pico-sdk .tinyusb.tmp .tinyusb
19+
20+
21+
.pico-sdk:
22+
rm -rf .pico-sdk.tmp
23+
git clone https://github.com/raspberrypi/pico-sdk .pico-sdk.tmp
24+
cd .pico-sdk.tmp \
25+
&& git checkout 62201a83e2693ea165fdc7669b4ab2f3b4f43c36 \
26+
&& git submodule update --init
27+
mv .pico-sdk.tmp .pico-sdk
28+
29+
30+
.tinyusb:
31+
rm -rf .tinyusb.tmp
32+
git clone https://github.com/hathach/tinyusb .tinyusb.tmp
33+
cd .tinyusb.tmp \
34+
&& git checkout c998e9c60bc76894006c3bd049d661124a9bfbfd \
35+
&& git submodule update --init
36+
mv .tinyusb.tmp .tinyusb

hid/pico/src/CMakeLists.txt

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
set(target_name hid)
2+
add_executable(${target_name})
3+
4+
target_sources(${target_name} PRIVATE
5+
main.c
6+
ph_outputs.c
7+
ph_usb.c
8+
ph_usb_kbd.c
9+
ph_usb_mouse.c
10+
ph_cmds.c
11+
ph_spi.c
12+
ph_debug.c
13+
)
14+
target_link_options(${target_name} PRIVATE -Xlinker --print-memory-usage)
15+
target_compile_options(${target_name} PRIVATE -Wall -Wextra)
16+
target_include_directories(${target_name} PRIVATE ${CMAKE_CURRENT_LIST_DIR})
17+
18+
target_link_libraries(${target_name} PRIVATE
19+
pico_stdlib
20+
pico_unique_id
21+
hardware_spi
22+
hardware_watchdog
23+
tinyusb_device
24+
)
25+
pico_add_extra_outputs(${target_name})

hid/pico/src/main.c

+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*****************************************************************************
2+
# #
3+
# KVMD - The main PiKVM daemon. #
4+
# #
5+
# Copyright (C) 2018-2023 Maxim Devaev <[email protected]> #
6+
# #
7+
# This program is free software: you can redistribute it and/or modify #
8+
# it under the terms of the GNU General Public License as published by #
9+
# the Free Software Foundation, either version 3 of the License, or #
10+
# (at your option) any later version. #
11+
# #
12+
# This program is distributed in the hope that it will be useful, #
13+
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
14+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15+
# GNU General Public License for more details. #
16+
# #
17+
# You should have received a copy of the GNU General Public License #
18+
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
19+
# #
20+
*****************************************************************************/
21+
22+
23+
#include "pico/stdlib.h"
24+
#include "hardware/gpio.h"
25+
#include "hardware/watchdog.h"
26+
27+
#include "ph_types.h"
28+
#include "ph_tools.h"
29+
#include "ph_outputs.h"
30+
#include "ph_usb.h"
31+
#include "ph_spi.h"
32+
#include "ph_proto.h"
33+
#include "ph_cmds.h"
34+
#include "ph_debug.h"
35+
36+
37+
static bool _reset_required = false;
38+
39+
40+
static u8 _handle_request(const u8 *data) { // 8 bytes
41+
// FIXME: See kvmd/kvmd#80
42+
// Should input buffer be cleared in this case?
43+
if (data[0] == PH_PROTO_MAGIC && ph_crc16(data, 6) == ph_merge8_u16(data[6], data[7])) {
44+
# define HANDLE(x_handler, x_reset) { \
45+
x_handler(data + 2); \
46+
if (x_reset) { _reset_required = true; } \
47+
return PH_PROTO_PONG_OK; \
48+
}
49+
switch (data[1]) {
50+
case PH_PROTO_CMD_PING: return PH_PROTO_PONG_OK;
51+
case PH_PROTO_CMD_SET_KBD: HANDLE(ph_cmd_set_kbd, true);
52+
case PH_PROTO_CMD_SET_MOUSE: HANDLE(ph_cmd_set_mouse, true);
53+
case PH_PROTO_CMD_SET_CONNECTED: return PH_PROTO_PONG_OK; // Arduino AUM
54+
case PH_PROTO_CMD_CLEAR_HID: HANDLE(ph_cmd_send_clear, false);
55+
case PH_PROTO_CMD_KBD_KEY: HANDLE(ph_cmd_kbd_send_key, false);
56+
case PH_PROTO_CMD_MOUSE_BUTTON: HANDLE(ph_cmd_mouse_send_button, false);
57+
case PH_PROTO_CMD_MOUSE_ABS: HANDLE(ph_cmd_mouse_send_abs, false);
58+
case PH_PROTO_CMD_MOUSE_REL: HANDLE(ph_cmd_mouse_send_rel, false);
59+
case PH_PROTO_CMD_MOUSE_WHEEL: HANDLE(ph_cmd_mouse_send_wheel, false);
60+
case PH_PROTO_CMD_REPEAT: return 0;
61+
}
62+
# undef HANDLE
63+
return PH_PROTO_RESP_INVALID_ERROR;
64+
}
65+
return PH_PROTO_RESP_CRC_ERROR;
66+
}
67+
68+
static void _send_response(u8 code) {
69+
static u8 prev_code = PH_PROTO_RESP_NONE;
70+
if (code == 0) {
71+
code = prev_code; // Repeat the last code
72+
} else {
73+
prev_code = code;
74+
}
75+
76+
u8 resp[8] = {0};
77+
resp[0] = PH_PROTO_MAGIC_RESP;
78+
79+
if (code & PH_PROTO_PONG_OK) {
80+
resp[1] = PH_PROTO_PONG_OK;
81+
if (_reset_required) {
82+
resp[1] |= PH_PROTO_PONG_RESET_REQUIRED;
83+
}
84+
resp[2] = PH_PROTO_OUT1_DYNAMIC;
85+
86+
resp[1] |= ph_cmd_get_offlines();
87+
resp[1] |= ph_cmd_kbd_get_leds();
88+
resp[2] |= ph_g_outputs_active;
89+
resp[3] |= ph_g_outputs_avail;
90+
} else {
91+
resp[1] = code;
92+
}
93+
94+
ph_split16(ph_crc16(resp, 6), &resp[6], &resp[7]);
95+
96+
ph_spi_write(resp);
97+
98+
if (_reset_required) {
99+
watchdog_reboot(0, 0, 100); // Даем немного времени чтобы отправить ответ, а потом ребутимся
100+
}
101+
}
102+
103+
static void _spi_data_handler(const u8 *data) {
104+
_send_response(_handle_request(data));
105+
}
106+
107+
108+
int main(void) {
109+
ph_debug_init(false); // No UART
110+
ph_outputs_init();
111+
ph_usb_init();
112+
ph_spi_init(_spi_data_handler);
113+
114+
while (true) {
115+
ph_usb_task();
116+
if (!_reset_required) {
117+
ph_spi_task();
118+
ph_debug_act_pulse(50);
119+
}
120+
}
121+
return 0;
122+
}

hid/pico/src/ph_cmds.c

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/* ========================================================================= #
2+
# #
3+
# KVMD - The main PiKVM daemon. #
4+
# #
5+
# Copyright (C) 2018-2023 Maxim Devaev <[email protected]> #
6+
# #
7+
# This program is free software: you can redistribute it and/or modify #
8+
# it under the terms of the GNU General Public License as published by #
9+
# the Free Software Foundation, either version 3 of the License, or #
10+
# (at your option) any later version. #
11+
# #
12+
# This program is distributed in the hope that it will be useful, #
13+
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
14+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15+
# GNU General Public License for more details. #
16+
# #
17+
# You should have received a copy of the GNU General Public License #
18+
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
19+
# #
20+
# ========================================================================= */
21+
22+
23+
#include "tusb.h"
24+
25+
#include "ph_types.h"
26+
#include "ph_tools.h"
27+
#include "ph_proto.h"
28+
#include "ph_outputs.h"
29+
#include "ph_usb.h"
30+
#include "ph_usb_keymap.h"
31+
32+
33+
u8 ph_cmd_kbd_get_leds(void) {
34+
u8 retval = 0;
35+
if (PH_O_IS_KBD_USB) {
36+
# define GET(x_mod) ((ph_g_usb_kbd_leds & KEYBOARD_LED_##x_mod##LOCK) ? PH_PROTO_PONG_##x_mod : 0)
37+
retval = GET(CAPS) | GET(SCROLL) | GET(NUM);
38+
# undef GET
39+
}
40+
return retval;
41+
}
42+
43+
u8 ph_cmd_get_offlines(void) {
44+
u8 retval = 0;
45+
if (PH_O_IS_KBD_USB) {
46+
if (!ph_g_usb_kbd_online) {
47+
retval |= PH_PROTO_PONG_KBD_OFFLINE;
48+
}
49+
}
50+
if (PH_O_IS_MOUSE_USB) {
51+
if (!ph_g_usb_mouse_online) {
52+
retval |= PH_PROTO_PONG_MOUSE_OFFLINE;
53+
}
54+
}
55+
return retval;
56+
}
57+
58+
void ph_cmd_set_kbd(const u8 *args) { // 1 byte
59+
ph_outputs_write(PH_PROTO_OUT1_KBD_MASK, args[0], false);
60+
}
61+
62+
void ph_cmd_set_mouse(const u8 *args) { // 1 byte
63+
ph_outputs_write(PH_PROTO_OUT1_MOUSE_MASK, args[0], false);
64+
}
65+
66+
void ph_cmd_send_clear(const u8 *args) { // 0 bytes
67+
(void)args;
68+
ph_usb_send_clear();
69+
}
70+
71+
void ph_cmd_kbd_send_key(const u8 *args) { // 2 bytes
72+
const u8 key = ph_usb_keymap(args[0]);
73+
if (key > 0) {
74+
ph_usb_kbd_send_key(key, args[1]);
75+
}
76+
}
77+
78+
void ph_cmd_mouse_send_button(const u8 *args) { // 2 bytes
79+
# define HANDLE(x_byte_n, x_button) { \
80+
if (args[x_byte_n] & PH_PROTO_CMD_MOUSE_##x_button##_SELECT) { \
81+
const bool m_state = !!(args[x_byte_n] & PH_PROTO_CMD_MOUSE_##x_button##_STATE); \
82+
ph_usb_mouse_send_button(MOUSE_BUTTON_##x_button, m_state); \
83+
} \
84+
}
85+
HANDLE(0, LEFT);
86+
HANDLE(0, RIGHT);
87+
HANDLE(0, MIDDLE);
88+
HANDLE(1, BACKWARD);
89+
HANDLE(1, FORWARD);
90+
# undef HANDLE
91+
}
92+
93+
void ph_cmd_mouse_send_abs(const u8 *args) { // 4 bytes
94+
ph_usb_mouse_send_abs(
95+
ph_merge8_s16(args[0], args[1]),
96+
ph_merge8_s16(args[2], args[3]));
97+
}
98+
99+
void ph_cmd_mouse_send_rel(const u8 *args) { // 2 bytes
100+
ph_usb_mouse_send_rel(args[0], args[1]);
101+
}
102+
103+
void ph_cmd_mouse_send_wheel(const u8 *args) { // 2 bytes
104+
ph_usb_mouse_send_wheel(args[0], args[1]);
105+
}

hid/pico/src/ph_cmds.h

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/* ========================================================================= #
2+
# #
3+
# KVMD - The main PiKVM daemon. #
4+
# #
5+
# Copyright (C) 2018-2023 Maxim Devaev <[email protected]> #
6+
# #
7+
# This program is free software: you can redistribute it and/or modify #
8+
# it under the terms of the GNU General Public License as published by #
9+
# the Free Software Foundation, either version 3 of the License, or #
10+
# (at your option) any later version. #
11+
# #
12+
# This program is distributed in the hope that it will be useful, #
13+
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
14+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15+
# GNU General Public License for more details. #
16+
# #
17+
# You should have received a copy of the GNU General Public License #
18+
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
19+
# #
20+
# ========================================================================= */
21+
22+
23+
#include "ph_types.h"
24+
25+
26+
u8 ph_cmd_kbd_get_leds(void);
27+
u8 ph_cmd_get_offlines(void);
28+
29+
void ph_cmd_set_kbd(const u8 *args);
30+
void ph_cmd_set_mouse(const u8 *args);
31+
32+
void ph_cmd_send_clear(const u8 *args);
33+
void ph_cmd_kbd_send_key(const u8 *args);
34+
void ph_cmd_mouse_send_button(const u8 *args);
35+
void ph_cmd_mouse_send_abs(const u8 *args);
36+
void ph_cmd_mouse_send_rel(const u8 *args);
37+
void ph_cmd_mouse_send_wheel(const u8 *args);

0 commit comments

Comments
 (0)