rp2040 hid

This commit is contained in:
Maxim Devaev 2023-08-03 05:47:27 +03:00
parent 1a8f98a64f
commit c96057772c
29 changed files with 1933 additions and 11 deletions

View File

@ -8,8 +8,14 @@
/testenv/.tox/
/testenv/.mypy_cache/
/testenv/.ssl/
/hid/.pio/
/hid/.platformio/
/hid/arduino/.pio/
/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/
/v*.tar.gz
/*.pkg.tar.xz

View File

@ -213,6 +213,7 @@ keymap: testenv
&& ./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-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
find kvmd testenv/tests -name __pycache__ | xargs rm -rf
make -C hid/arduino clean
make -C hid/pico clean
clean-all: testenv clean
make -C hid/arduino clean-all
make -C hid/pico clean-all
- $(DOCKER) run --rm \
--volume `pwd`:/src \
-it $(TESTENV_IMAGE) bash -c "cd src && rm -rf testenv/{.ssl,.tox,.mypy_cache,.coverage}"

6
hid/pico/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
/.pico-sdk.tmp/
/.pico-sdk/
/.tinyusb.tmp/
/.tinyusb/
/.build/
/*.uf2

21
hid/pico/CMakeLists.txt Normal file
View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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;

View 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;
}

View 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
View 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);

View 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;

View 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

View File

@ -108,6 +108,7 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many-
reset_pin: int,
reset_inverted: bool,
reset_delay: float,
reset_self: bool,
read_retries: int,
common_retries: int,
@ -126,6 +127,7 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many-
self.__phy = phy
self.__gpio = Gpio(gpio_device_path, reset_pin, reset_inverted, reset_delay)
self.__reset_self = reset_self
self.__reset_required_event = multiprocessing.Event()
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_inverted": Option(False, type=valid_bool),
"reset_delay": Option(0.1, type=valid_float_f01),
"reset_self": Option(False, type=valid_bool),
"read_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)
self.__state_flags.update(online=1, busy=reset_required, status=status)
if reset_required:
self.__reset_required_event.set()
if self.__reset_self:
time.sleep(1) # Pico перезагружается сам вскоре после ответа
else:
self.__reset_required_event.set()

View File

@ -100,6 +100,7 @@ class _SpiPhy(BasePhy): # pylint: disable=too-many-instance-attributes
chip: int,
hw_cs: bool,
sw_cs_pin: int,
sw_cs_per_byte: bool,
max_freq: int,
block_usec: int,
read_timeout: float,
@ -110,6 +111,7 @@ class _SpiPhy(BasePhy): # pylint: disable=too-many-instance-attributes
self.__chip = chip
self.__hw_cs = hw_cs
self.__sw_cs_pin = sw_cs_pin
self.__sw_cs_per_byte = sw_cs_per_byte
self.__max_freq = max_freq
self.__block_usec = block_usec
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.max_speed_hz = self.__max_freq
def xfer(data: bytes) -> bytes:
def inner_xfer(data: bytes) -> bytes:
try:
if sw_cs_line is not None:
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:
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(
xfer=xfer,
read_timeout=self.__read_timeout,
@ -167,11 +180,12 @@ class Plugin(BaseMcuHid):
@classmethod
def __get_phy_options(cls) -> dict:
return {
"bus": Option(-1, type=valid_int_f0),
"chip": Option(-1, type=valid_int_f0),
"hw_cs": Option(False, type=valid_bool),
"sw_cs_pin": Option(-1, type=valid_gpio_pin_optional),
"max_freq": Option(100000, type=valid_int_f1),
"block_usec": Option(1, type=valid_int_f0),
"read_timeout": Option(0.5, type=valid_float_f01),
"bus": Option(-1, type=valid_int_f0),
"chip": Option(-1, type=valid_int_f0),
"hw_cs": Option(False, type=valid_bool),
"sw_cs_pin": Option(-1, type=valid_gpio_pin_optional),
"sw_cs_per_byte": Option(False, type=valid_bool),
"max_freq": Option(100000, type=valid_int_f1),
"block_usec": Option(1, type=valid_int_f0),
"read_timeout": Option(0.5, type=valid_float_f01),
}