spi firmware

This commit is contained in:
Devaev Maxim 2020-11-03 10:15:54 +03:00
parent a3707d047f
commit e07cdd60f3
15 changed files with 254 additions and 195 deletions

View File

@ -27,6 +27,5 @@ def _patch_lib(lib_path: str, patch_path: str) -> None:
# ===== # =====
_libs = _get_libs() _libs = _get_libs()
assert "TimerOne" in _libs # Just checking
if "HID-Project" in _libs: if "HID-Project" in _libs:
_patch_lib(_libs["HID-Project"], "patches/absmouse.patch") _patch_lib(_libs["HID-Project"], "patches/absmouse.patch")

View File

@ -12,10 +12,6 @@ extra_scripts =
platform_packages = platform_packages =
tool-avrdude tool-avrdude
[_parts_common]
lib_deps =
TimerOne@1.1
[_parts_usb_kbd] [_parts_usb_kbd]
lib_deps = lib_deps =
HID-Project@2.6.1 HID-Project@2.6.1
@ -38,7 +34,6 @@ build_flags =
[_usb] [_usb]
lib_deps = lib_deps =
${_parts_common.lib_deps}
${_parts_usb_kbd.lib_deps} ${_parts_usb_kbd.lib_deps}
# ${_parts_usb_mouse.lib_deps} # ${_parts_usb_mouse.lib_deps}
build_flags = build_flags =
@ -47,14 +42,12 @@ build_flags =
[_ps2] [_ps2]
lib_deps = lib_deps =
${_parts_common.lib_deps}
${_parts_ps2_kbd.lib_deps} ${_parts_ps2_kbd.lib_deps}
build_flags = build_flags =
${_parts_ps2_kbd.build_flags} ${_parts_ps2_kbd.build_flags}
[_mixed] [_mixed]
lib_deps = lib_deps =
${_parts_common.lib_deps}
${_parts_ps2_kbd.lib_deps} ${_parts_ps2_kbd.lib_deps}
${_parts_usb_mouse.lib_deps} ${_parts_usb_mouse.lib_deps}
build_flags = build_flags =
@ -107,9 +100,7 @@ extra_scripts =
[_cmd_spi] [_cmd_spi]
build_flags = build_flags =
-DCMD_SERIAL=Serial1 -DCMD_SPI
-DCMD_SERIAL_SPEED=115200
# -DCMD_SPI
upload_protocol = custom upload_protocol = custom
upload_flags = upload_flags =
-C -C

View File

@ -1,26 +0,0 @@
/*****************************************************************************
# #
# KVMD - The main Pi-KVM daemon. #
# #
# Copyright (C) 2018 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
#define INLINE inline __attribute__((always_inline))

View File

@ -20,10 +20,17 @@
*****************************************************************************/ *****************************************************************************/
#include <Arduino.h> #if !(defined(CMD_SERIAL) || defined(CMD_SPI))
#include <TimerOne.h> # error CMD phy is not defined
#endif
#include "inline.h"
#include <Arduino.h>
#ifdef CMD_SPI
# include <SPI.h>
#endif
#include "proto.h"
#if defined(HID_USB_KBD) || defined(HID_USB_MOUSE) #if defined(HID_USB_KBD) || defined(HID_USB_MOUSE)
# include "usb/hid.h" # include "usb/hid.h"
@ -35,41 +42,9 @@
// #define CMD_SERIAL Serial1 // #define CMD_SERIAL Serial1
// #define CMD_SERIAL_SPEED 115200 // #define CMD_SERIAL_SPEED 115200
#define CMD_RECV_TIMEOUT 100000 // -- OR --
// #define CMD_SPI
#define PROTO_MAGIC 0x33 #define CMD_TIMEOUT 100000
#define PROTO_CRC_POLINOM 0xA001
#define PROTO_RESP_OK 0x20
#define PROTO_RESP_NONE 0x24
#define PROTO_RESP_CRC_ERROR 0x40
#define PROTO_RESP_INVALID_ERROR 0x45
#define PROTO_RESP_TIMEOUT_ERROR 0x48
#define PROTO_RESP_PONG_PREFIX 0x80
#define PROTO_RESP_PONG_CAPS 0b00000001
#define PROTO_RESP_PONG_SCROLL 0b00000010
#define PROTO_RESP_PONG_NUM 0b00000100
#define PROTO_CMD_PING 0x01
#define PROTO_CMD_REPEAT 0x02
#define PROTO_CMD_RESET_HID 0x10
#define PROTO_CMD_KEY_EVENT 0x11
#define PROTO_CMD_MOUSE_BUTTON_EVENT 0x13 // Legacy sequence
#define PROTO_CMD_MOUSE_MOVE_EVENT 0x12
#define PROTO_CMD_MOUSE_WHEEL_EVENT 0x14
#define PROTO_CMD_MOUSE_BUTTON_LEFT_SELECT 0b10000000
#define PROTO_CMD_MOUSE_BUTTON_LEFT_STATE 0b00001000
#define PROTO_CMD_MOUSE_BUTTON_RIGHT_SELECT 0b01000000
#define PROTO_CMD_MOUSE_BUTTON_RIGHT_STATE 0b00000100
#define PROTO_CMD_MOUSE_BUTTON_MIDDLE_SELECT 0b00100000
#define PROTO_CMD_MOUSE_BUTTON_MIDDLE_STATE 0b00000010
#define PROTO_CMD_MOUSE_BUTTON_EXTRA_UP_SELECT 0b10000000
#define PROTO_CMD_MOUSE_BUTTON_EXTRA_UP_STATE 0b00001000
#define PROTO_CMD_MOUSE_BUTTON_EXTRA_DOWN_SELECT 0b01000000
#define PROTO_CMD_MOUSE_BUTTON_EXTRA_DOWN_STATE 0b00000100
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -84,7 +59,7 @@
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
INLINE uint8_t cmdResetHid(const uint8_t *buffer) { // 0 bytes uint8_t cmdResetHid(const uint8_t *buffer) { // 0 bytes
# ifdef HID_USB_KBD # ifdef HID_USB_KBD
hid_kbd.reset(); hid_kbd.reset();
# endif # endif
@ -94,12 +69,12 @@ INLINE uint8_t cmdResetHid(const uint8_t *buffer) { // 0 bytes
return PROTO_RESP_OK; return PROTO_RESP_OK;
} }
INLINE uint8_t cmdKeyEvent(const uint8_t *buffer) { // 2 bytes uint8_t cmdKeyEvent(const uint8_t *buffer) { // 2 bytes
hid_kbd.sendKey(buffer[0], buffer[1]); hid_kbd.sendKey(buffer[0], buffer[1]);
return PROTO_RESP_OK; return PROTO_RESP_OK;
} }
INLINE uint8_t cmdMouseButtonEvent(const uint8_t *buffer) { // 2 bytes uint8_t cmdMouseButtonEvent(const uint8_t *buffer) { // 2 bytes
# ifdef HID_USB_MOUSE # ifdef HID_USB_MOUSE
uint8_t main_state = buffer[0]; uint8_t main_state = buffer[0];
uint8_t extra_state = buffer[1]; uint8_t extra_state = buffer[1];
@ -119,7 +94,7 @@ INLINE uint8_t cmdMouseButtonEvent(const uint8_t *buffer) { // 2 bytes
return PROTO_RESP_OK; return PROTO_RESP_OK;
} }
INLINE uint8_t cmdMouseMoveEvent(const uint8_t *buffer) { // 4 bytes uint8_t cmdMouseMoveEvent(const uint8_t *buffer) { // 4 bytes
# ifdef HID_USB_MOUSE # ifdef HID_USB_MOUSE
int x = (int)buffer[0] << 8; int x = (int)buffer[0] << 8;
x |= (int)buffer[1]; x |= (int)buffer[1];
@ -134,14 +109,14 @@ INLINE uint8_t cmdMouseMoveEvent(const uint8_t *buffer) { // 4 bytes
return PROTO_RESP_OK; return PROTO_RESP_OK;
} }
INLINE uint8_t cmdMouseWheelEvent(const uint8_t *buffer) { // 2 bytes uint8_t cmdMouseWheelEvent(const uint8_t *buffer) { // 2 bytes
# ifdef HID_USB_MOUSE # ifdef HID_USB_MOUSE
hid_mouse.sendMouseWheel(buffer[1]); // Y only, X is not supported hid_mouse.sendMouseWheel(buffer[1]); // Y only, X is not supported
# endif # endif
return PROTO_RESP_OK; return PROTO_RESP_OK;
} }
INLINE uint8_t cmdPongLeds(const uint8_t *buffer) { // 0 bytes uint8_t cmdPongLeds(const uint8_t *buffer) { // 0 bytes
return ((uint8_t) PROTO_RESP_PONG_PREFIX) | hid_kbd.getLedsAs( return ((uint8_t) PROTO_RESP_PONG_PREFIX) | hid_kbd.getLedsAs(
PROTO_RESP_PONG_CAPS, PROTO_RESP_PONG_CAPS,
PROTO_RESP_PONG_SCROLL, PROTO_RESP_PONG_SCROLL,
@ -149,40 +124,86 @@ INLINE uint8_t cmdPongLeds(const uint8_t *buffer) { // 0 bytes
); );
} }
uint8_t handleCmdBuffer(const uint8_t *buffer) { // 8 bytes
uint16_t crc = (uint16_t)buffer[6] << 8;
crc |= (uint16_t)buffer[7];
// ----------------------------------------------------------------------------- if (protoCrc16(buffer, 6) == crc) {
INLINE uint16_t makeCrc16(const uint8_t *buffer, unsigned length) { # define HANDLE(_handler) { return _handler(buffer + 2); }
uint16_t crc = 0xFFFF; switch (buffer[1]) {
case PROTO_CMD_RESET_HID: HANDLE(cmdResetHid);
for (unsigned byte_count = 0; byte_count < length; ++byte_count) { case PROTO_CMD_KEY_EVENT: HANDLE(cmdKeyEvent);
crc = crc ^ buffer[byte_count]; case PROTO_CMD_MOUSE_BUTTON_EVENT: HANDLE(cmdMouseButtonEvent);
for (unsigned bit_count = 0; bit_count < 8; ++bit_count) { case PROTO_CMD_MOUSE_MOVE_EVENT: HANDLE(cmdMouseMoveEvent);
if ((crc & 0x0001) == 0) { case PROTO_CMD_MOUSE_WHEEL_EVENT: HANDLE(cmdMouseWheelEvent);
crc = crc >> 1; case PROTO_CMD_PING: HANDLE(cmdPongLeds);
} else { case PROTO_CMD_REPEAT: return 0;
crc = crc >> 1; default: return PROTO_RESP_INVALID_ERROR;
crc = crc ^ PROTO_CRC_POLINOM;
}
} }
# undef HANDLE
} }
return crc; return PROTO_RESP_CRC_ERROR;
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
volatile bool cmd_recv_timed_out = false; #ifdef CMD_SPI
volatile uint8_t spi_in[8] = {0};
volatile uint8_t spi_in_index = 0;
volatile uint8_t spi_in_read = 0; // Вычитанное spiRead()
INLINE void recvTimerStop(bool flag) { volatile uint8_t spi_out[4] = {0};
Timer1.stop(); volatile uint8_t spi_out_index = 0;
cmd_recv_timed_out = flag;
uint8_t spiAvailable() {
return spi_in_index - spi_in_read;
} }
INLINE void resetCmdRecvTimeout() { uint8_t spiRead() {
recvTimerStop(false); uint8_t value = 0;
Timer1.initialize(CMD_RECV_TIMEOUT); if (spi_in_read < 8) {
value = spi_in[spi_in_read];
++spi_in_read;
}
return value;
} }
INLINE void sendCmdResponse(uint8_t code=0) { void spiWrite(const uint8_t *buffer) {
if (spi_out[0] == 0) {
spi_out[3] = buffer[3];
spi_out[2] = buffer[2];
spi_out[1] = buffer[1];
spi_out[0] = buffer[0]; // Меджик разрешает начать ответ
}
}
void spiReadReset() {
spi_in_index = 0;
spi_in_read = 0;
}
ISR(SPI_STC_vect) {
if (spi_in_index < 8) {
spi_in[spi_in_index] = SPDR;
++spi_in_index;
SPDR = 0;
} else if (spi_out[0] && spi_out_index < 4) {
SPDR = spi_out[spi_out_index];
++spi_out_index;
if (spi_out_index == 4) {
spiReadReset();
spi_out[0] = 0;
spi_out_index = 0;
}
} else {
SPDR = 0;
}
}
#endif
// -----------------------------------------------------------------------------
void sendCmdResponse(uint8_t code) {
static uint8_t prev_code = PROTO_RESP_NONE; static uint8_t prev_code = PROTO_RESP_NONE;
if (code == 0) { if (code == 0) {
code = prev_code; // Repeat the last code code = prev_code; // Repeat the last code
@ -193,16 +214,23 @@ INLINE void sendCmdResponse(uint8_t code=0) {
uint8_t buffer[4]; uint8_t buffer[4];
buffer[0] = PROTO_MAGIC; buffer[0] = PROTO_MAGIC;
buffer[1] = code; buffer[1] = code;
uint16_t crc = makeCrc16(buffer, 2); uint16_t crc = protoCrc16(buffer, 2);
buffer[2] = (uint8_t)(crc >> 8); buffer[2] = (uint8_t)(crc >> 8);
buffer[3] = (uint8_t)(crc & 0xFF); buffer[3] = (uint8_t)(crc & 0xFF);
recvTimerStop(false); # ifdef CMD_SERIAL
CMD_SERIAL.write(buffer, 4); CMD_SERIAL.write(buffer, 4);
# elif defined(CMD_SPI)
spiWrite(buffer);
# endif
} }
void intRecvTimedOut() { bool isCmdTimedOut(unsigned long last) {
recvTimerStop(true); unsigned long now = micros();
return (
(now >= last && now - last > CMD_TIMEOUT)
|| (now < last && ((unsigned long)-1) - last + now > CMD_TIMEOUT)
);
} }
void setup() { void setup() {
@ -211,48 +239,44 @@ void setup() {
hid_mouse.begin(); hid_mouse.begin();
# endif # endif
Timer1.attachInterrupt(intRecvTimedOut); # ifdef CMD_SERIAL
CMD_SERIAL.begin(CMD_SERIAL_SPEED); CMD_SERIAL.begin(CMD_SERIAL_SPEED);
# elif defined(CMD_SPI)
pinMode(MISO, OUTPUT);
SPCR = (1 << SPE) | (1 << SPIE); // Slave, SPI En, IRQ En
# endif
} }
void loop() { void loop() {
unsigned long last = micros();
uint8_t buffer[8]; uint8_t buffer[8];
unsigned index = 0; uint8_t index = 0;
while (true) { while (true) {
# ifdef HID_PS2_KBD # ifdef HID_PS2_KBD
hid_kbd.periodic(); hid_kbd.periodic();
# endif # endif
# ifdef CMD_SERIAL
if (CMD_SERIAL.available() > 0) { if (CMD_SERIAL.available() > 0) {
buffer[index] = (uint8_t)CMD_SERIAL.read(); buffer[index] = (uint8_t)CMD_SERIAL.read();
# elif defined(CMD_SPI)
if (spiAvailable() > 0) {
buffer[index] = spiRead();
# endif
if (index == 7) { if (index == 7) {
uint16_t crc = (uint16_t)buffer[6] << 8; sendCmdResponse(handleCmdBuffer(buffer));
crc |= (uint16_t)buffer[7];
if (makeCrc16(buffer, 6) == crc) {
# define HANDLE(_handler) { sendCmdResponse(_handler(buffer + 2)); break; }
switch (buffer[1]) {
case PROTO_CMD_RESET_HID: HANDLE(cmdResetHid);
case PROTO_CMD_KEY_EVENT: HANDLE(cmdKeyEvent);
case PROTO_CMD_MOUSE_BUTTON_EVENT: HANDLE(cmdMouseButtonEvent);
case PROTO_CMD_MOUSE_MOVE_EVENT: HANDLE(cmdMouseMoveEvent);
case PROTO_CMD_MOUSE_WHEEL_EVENT: HANDLE(cmdMouseWheelEvent);
case PROTO_CMD_PING: HANDLE(cmdPongLeds);
case PROTO_CMD_REPEAT: sendCmdResponse(); break;
default: sendCmdResponse(PROTO_RESP_INVALID_ERROR); break;
}
# undef HANDLE
} else {
sendCmdResponse(PROTO_RESP_CRC_ERROR);
}
index = 0; index = 0;
} else { } else {
resetCmdRecvTimeout(); last = micros();
index += 1; ++index;
} }
} else if (index > 0 && cmd_recv_timed_out) { } else if (index > 0 && isCmdTimedOut(last)) {
# ifdef CMD_SERIAL
sendCmdResponse(PROTO_RESP_TIMEOUT_ERROR); sendCmdResponse(PROTO_RESP_TIMEOUT_ERROR);
# elif defined(CMD_SPI)
spiReadReset();
# endif
index = 0; index = 0;
} }
} }

76
hid/src/proto.h Normal file
View File

@ -0,0 +1,76 @@
/*****************************************************************************
# #
# KVMD - The main Pi-KVM daemon. #
# #
# Copyright (C) 2018 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
#define PROTO_MAGIC 0x33
#define PROTO_CRC_POLINOM 0xA001
#define PROTO_RESP_OK 0x20
#define PROTO_RESP_NONE 0x24
#define PROTO_RESP_CRC_ERROR 0x40
#define PROTO_RESP_INVALID_ERROR 0x45
#define PROTO_RESP_TIMEOUT_ERROR 0x48
#define PROTO_RESP_PONG_PREFIX 0x80
#define PROTO_RESP_PONG_CAPS 0b00000001
#define PROTO_RESP_PONG_SCROLL 0b00000010
#define PROTO_RESP_PONG_NUM 0b00000100
#define PROTO_CMD_PING 0x01
#define PROTO_CMD_REPEAT 0x02
#define PROTO_CMD_RESET_HID 0x10
#define PROTO_CMD_KEY_EVENT 0x11
#define PROTO_CMD_MOUSE_BUTTON_EVENT 0x13 // Legacy sequence
#define PROTO_CMD_MOUSE_MOVE_EVENT 0x12
#define PROTO_CMD_MOUSE_WHEEL_EVENT 0x14
#define PROTO_CMD_MOUSE_BUTTON_LEFT_SELECT 0b10000000
#define PROTO_CMD_MOUSE_BUTTON_LEFT_STATE 0b00001000
#define PROTO_CMD_MOUSE_BUTTON_RIGHT_SELECT 0b01000000
#define PROTO_CMD_MOUSE_BUTTON_RIGHT_STATE 0b00000100
#define PROTO_CMD_MOUSE_BUTTON_MIDDLE_SELECT 0b00100000
#define PROTO_CMD_MOUSE_BUTTON_MIDDLE_STATE 0b00000010
#define PROTO_CMD_MOUSE_BUTTON_EXTRA_UP_SELECT 0b10000000
#define PROTO_CMD_MOUSE_BUTTON_EXTRA_UP_STATE 0b00001000
#define PROTO_CMD_MOUSE_BUTTON_EXTRA_DOWN_SELECT 0b01000000
#define PROTO_CMD_MOUSE_BUTTON_EXTRA_DOWN_STATE 0b00000100
uint16_t protoCrc16(const uint8_t *buffer, unsigned length) {
uint16_t crc = 0xFFFF;
for (unsigned byte_count = 0; byte_count < length; ++byte_count) {
crc = crc ^ buffer[byte_count];
for (unsigned bit_count = 0; bit_count < 8; ++bit_count) {
if ((crc & 0x0001) == 0) {
crc = crc >> 1;
} else {
crc = crc >> 1;
crc = crc ^ PROTO_CRC_POLINOM;
}
}
}
return crc;
}

View File

@ -25,8 +25,6 @@
#include <Arduino.h> #include <Arduino.h>
#include <ps2dev.h> #include <ps2dev.h>
#include "../inline.h"
#include "keymap.h" #include "keymap.h"
// #define PS2_KBD_CLOCK_PIN 7 // #define PS2_KBD_CLOCK_PIN 7
@ -43,11 +41,11 @@ class Ps2HidKeyboard {
_dev.keyboard_init(); _dev.keyboard_init();
} }
INLINE void periodic() { void periodic() {
_dev.keyboard_handle(&_leds); _dev.keyboard_handle(&_leds);
} }
INLINE void sendKey(uint8_t code, bool state) { void sendKey(uint8_t code, bool state) {
Ps2KeyType ps2_type; Ps2KeyType ps2_type;
uint8_t ps2_code; uint8_t ps2_code;
@ -76,7 +74,7 @@ class Ps2HidKeyboard {
} }
} }
INLINE uint8_t getLedsAs(uint8_t caps, uint8_t scroll, uint8_t num) { uint8_t getLedsAs(uint8_t caps, uint8_t scroll, uint8_t num) {
uint8_t result = 0; uint8_t result = 0;
periodic(); periodic();

View File

@ -22,8 +22,6 @@
#pragma once #pragma once
#include "../inline.h"
enum Ps2KeyType : uint8_t { enum Ps2KeyType : uint8_t {
PS2_KEY_TYPE_UNKNOWN = 0, PS2_KEY_TYPE_UNKNOWN = 0,
@ -34,7 +32,7 @@ enum Ps2KeyType : uint8_t {
}; };
INLINE void keymapPs2(uint8_t code, Ps2KeyType *ps2_type, uint8_t *ps2_code) { void keymapPs2(uint8_t code, Ps2KeyType *ps2_type, uint8_t *ps2_code) {
*ps2_type = PS2_KEY_TYPE_UNKNOWN; *ps2_type = PS2_KEY_TYPE_UNKNOWN;
*ps2_code = 0; *ps2_code = 0;

View File

@ -22,8 +22,6 @@
#pragma once #pragma once
#include "../inline.h"
enum Ps2KeyType : uint8_t { enum Ps2KeyType : uint8_t {
PS2_KEY_TYPE_UNKNOWN = 0, PS2_KEY_TYPE_UNKNOWN = 0,
@ -34,7 +32,7 @@ enum Ps2KeyType : uint8_t {
}; };
<%! import operator %> <%! import operator %>
INLINE void keymapPs2(uint8_t code, Ps2KeyType *ps2_type, uint8_t *ps2_code) { void keymapPs2(uint8_t code, Ps2KeyType *ps2_type, uint8_t *ps2_code) {
*ps2_type = PS2_KEY_TYPE_UNKNOWN; *ps2_type = PS2_KEY_TYPE_UNKNOWN;
*ps2_code = 0; *ps2_code = 0;

View File

@ -24,8 +24,6 @@
#include <HID-Project.h> #include <HID-Project.h>
#include "../inline.h"
#include "keymap.h" #include "keymap.h"
@ -38,11 +36,11 @@ class UsbHidKeyboard {
BootKeyboard.begin(); BootKeyboard.begin();
} }
INLINE void reset() { void reset() {
BootKeyboard.releaseAll(); BootKeyboard.releaseAll();
} }
INLINE void sendKey(uint8_t code, bool state) { void sendKey(uint8_t code, bool state) {
KeyboardKeycode usb_code = keymapUsb(code); KeyboardKeycode usb_code = keymapUsb(code);
if (usb_code != KEY_ERROR_UNDEFINED) { if (usb_code != KEY_ERROR_UNDEFINED) {
if (state) BootKeyboard.press(usb_code); if (state) BootKeyboard.press(usb_code);
@ -50,7 +48,7 @@ class UsbHidKeyboard {
} }
} }
INLINE uint8_t getLedsAs(uint8_t caps, uint8_t scroll, uint8_t num) { uint8_t getLedsAs(uint8_t caps, uint8_t scroll, uint8_t num) {
uint8_t leds = BootKeyboard.getLeds(); uint8_t leds = BootKeyboard.getLeds();
uint8_t result = 0; uint8_t result = 0;
@ -69,11 +67,11 @@ class UsbHidMouse {
SingleAbsoluteMouse.begin(); SingleAbsoluteMouse.begin();
} }
INLINE void reset() { void reset() {
SingleAbsoluteMouse.releaseAll(); SingleAbsoluteMouse.releaseAll();
} }
INLINE void sendMouseButtons( void sendMouseButtons(
bool left_select, bool left_state, bool left_select, bool left_state,
bool right_select, bool right_state, bool right_select, bool right_state,
bool middle_select, bool middle_state, bool middle_select, bool middle_state,
@ -87,17 +85,17 @@ class UsbHidMouse {
if (down_select) _sendMouseButton(MOUSE_NEXT, down_state); if (down_select) _sendMouseButton(MOUSE_NEXT, down_state);
} }
INLINE void sendMouseMove(int x, int y) { void sendMouseMove(int x, int y) {
SingleAbsoluteMouse.moveTo(x, y); SingleAbsoluteMouse.moveTo(x, y);
} }
INLINE void sendMouseWheel(int delta_y) { void sendMouseWheel(int delta_y) {
// delta_x is not supported by hid-project now // delta_x is not supported by hid-project now
SingleAbsoluteMouse.move(0, 0, delta_y); SingleAbsoluteMouse.move(0, 0, delta_y);
} }
private: private:
INLINE void _sendMouseButton(uint8_t button, bool state) { void _sendMouseButton(uint8_t button, bool state) {
if (state) SingleAbsoluteMouse.press(button); if (state) SingleAbsoluteMouse.press(button);
else SingleAbsoluteMouse.release(button); else SingleAbsoluteMouse.release(button);
} }

View File

@ -24,10 +24,8 @@
#include <HID-Project.h> #include <HID-Project.h>
#include "../inline.h"
KeyboardKeycode keymapUsb(uint8_t code) {
INLINE KeyboardKeycode keymapUsb(uint8_t code) {
switch (code) { switch (code) {
case 1: return KEY_A; case 1: return KEY_A;
case 2: return KEY_B; case 2: return KEY_B;

View File

@ -24,10 +24,8 @@
#include <HID-Project.h> #include <HID-Project.h>
#include "../inline.h"
<%! import operator %> <%! import operator %>
INLINE KeyboardKeycode keymapUsb(uint8_t code) { KeyboardKeycode keymapUsb(uint8_t code) {
switch (code) { switch (code) {
% for km in sorted(keymap, key=operator.attrgetter("mcu_code")): % for km in sorted(keymap, key=operator.attrgetter("mcu_code")):
case ${km.mcu_code}: return ${km.arduino_name}; case ${km.mcu_code}: return ${km.arduino_name};

View File

@ -34,12 +34,12 @@ from . import aiotools
# ===== # =====
async def pulse(line: gpiod.Line, delay: float, final: float) -> None: async def pulse(line: gpiod.Line, delay: float, final: float, inverted: bool=False) -> None:
try: try:
line.set_value(1) line.set_value(int(not inverted))
await asyncio.sleep(delay) await asyncio.sleep(delay)
finally: finally:
line.set_value(0) line.set_value(int(inverted))
await asyncio.sleep(final) await asyncio.sleep(final)

View File

@ -172,6 +172,7 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many-
phy: BasePhy, phy: BasePhy,
reset_pin: int, reset_pin: int,
reset_inverted: bool,
reset_delay: float, reset_delay: float,
read_retries: int, read_retries: int,
@ -190,7 +191,7 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many-
self.__noop = noop self.__noop = noop
self.__phy = phy self.__phy = phy
self.__gpio = Gpio(reset_pin, reset_delay) self.__gpio = Gpio(reset_pin, reset_inverted, reset_delay)
self.__events_queue: "multiprocessing.Queue[_BaseEvent]" = multiprocessing.Queue() self.__events_queue: "multiprocessing.Queue[_BaseEvent]" = multiprocessing.Queue()
@ -207,8 +208,9 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many-
@classmethod @classmethod
def get_plugin_options(cls) -> Dict: def get_plugin_options(cls) -> Dict:
return { return {
"reset_pin": Option(-1, type=valid_gpio_pin_optional), "reset_pin": Option(-1, type=valid_gpio_pin_optional),
"reset_delay": Option(0.1, type=valid_float_f01), "reset_inverted": Option(False, type=valid_bool),
"reset_delay": Option(0.1, type=valid_float_f01),
"read_retries": Option(10, type=valid_int_f1), "read_retries": Option(10, type=valid_int_f1),
"common_retries": Option(100, type=valid_int_f1), "common_retries": Option(100, type=valid_int_f1),

View File

@ -33,8 +33,15 @@ from .... import aiogp
# ===== # =====
class Gpio: class Gpio:
def __init__(self, reset_pin: int, reset_delay: float) -> None: def __init__(
self,
reset_pin: int,
reset_inverted: bool,
reset_delay: float,
) -> None:
self.__reset_pin = reset_pin self.__reset_pin = reset_pin
self.__reset_inverted = reset_inverted
self.__reset_delay = reset_delay self.__reset_delay = reset_delay
self.__chip: Optional[gpiod.Chip] = None self.__chip: Optional[gpiod.Chip] = None
@ -47,7 +54,7 @@ class Gpio:
assert self.__reset_line is None assert self.__reset_line is None
self.__chip = gpiod.Chip(env.GPIO_DEVICE_PATH) self.__chip = gpiod.Chip(env.GPIO_DEVICE_PATH)
self.__reset_line = self.__chip.get_line(self.__reset_pin) self.__reset_line = self.__chip.get_line(self.__reset_pin)
self.__reset_line.request("kvmd::hid-mcu::reset", gpiod.LINE_REQ_DIR_OUT, default_vals=[0]) self.__reset_line.request("kvmd::hid-mcu::reset", gpiod.LINE_REQ_DIR_OUT, default_vals=[int(self.__reset_inverted)])
def close(self) -> None: def close(self) -> None:
if self.__chip: if self.__chip:
@ -63,7 +70,7 @@ class Gpio:
if not self.__reset_wip: if not self.__reset_wip:
self.__reset_wip = True self.__reset_wip = True
try: try:
await aiogp.pulse(self.__reset_line, self.__reset_delay, 1) await aiogp.pulse(self.__reset_line, self.__reset_delay, 1, self.__reset_inverted)
finally: finally:
self.__reset_wip = False self.__reset_wip = False
get_logger(0).info("Reset HID performed") get_logger(0).info("Reset HID performed")

View File

@ -27,6 +27,7 @@ import time
from typing import List from typing import List
from typing import Dict from typing import Dict
from typing import Generator from typing import Generator
from typing import Callable
from typing import Any from typing import Any
import spidev import spidev
@ -49,31 +50,18 @@ from ._mcu import BaseMcuHid
class _SpiPhyConnection(BasePhyConnection): class _SpiPhyConnection(BasePhyConnection):
def __init__( def __init__(
self, self,
spi: spidev.SpiDev, xfer: Callable[[bytes], bytes],
read_timeout: float, read_timeout: float,
read_delay: float, read_delay: float,
) -> None: ) -> None:
self.__spi = spi self.__xfer = xfer
self.__read_timeout = read_timeout self.__read_timeout = read_timeout
self.__read_delay = read_delay self.__read_delay = read_delay
self.__empty8 = b"\x00" * 8
self.__empty4 = b"\x00" * 4
def send(self, request: bytes) -> bytes: def send(self, request: bytes) -> bytes:
assert len(request) == 8 assert len(request) == 8
self.__xfer(request)
deadline_ts = time.time() + self.__read_timeout
while time.time() < deadline_ts:
garbage = bytes(self.__spi.xfer(self.__empty8))
if garbage == self.__empty8:
break
else:
get_logger(0).error("SPI timeout reached while reading the a garbage")
return b""
self.__spi.xfer(request)
response: List[int] = [] response: List[int] = []
deadline_ts = time.time() + self.__read_timeout deadline_ts = time.time() + self.__read_timeout
@ -81,21 +69,19 @@ class _SpiPhyConnection(BasePhyConnection):
while time.time() < deadline_ts: while time.time() < deadline_ts:
if not found: if not found:
time.sleep(self.__read_delay) time.sleep(self.__read_delay)
for byte in self.__spi.xfer(self.__empty4): for byte in self.__xfer(b"\x00" * (4 - len(response))):
if not found: if not found:
if byte == 0: if byte == 0:
continue continue
found = True found = True
response.append(byte) response.append(byte)
if len(response) >= 4: if len(response) == 4:
break break
if len(response) >= 4: if len(response) == 4:
break break
else: else:
get_logger(0).error("SPI timeout reached while responce waiting") get_logger(0).error("SPI timeout reached while responce waiting")
return b"" return b""
assert len(response) == 4
return bytes(response) return bytes(response)
@ -105,6 +91,7 @@ class _SpiPhy(BasePhy):
bus: int, bus: int,
chip: int, chip: int,
max_freq: int, max_freq: int,
block_usec: int,
read_timeout: float, read_timeout: float,
read_delay: float, read_delay: float,
) -> None: ) -> None:
@ -112,6 +99,7 @@ class _SpiPhy(BasePhy):
self.__bus = bus self.__bus = bus
self.__chip = chip self.__chip = chip
self.__max_freq = max_freq self.__max_freq = max_freq
self.__block_usec = block_usec
self.__read_timeout = read_timeout self.__read_timeout = read_timeout
self.__read_delay = read_delay self.__read_delay = read_delay
@ -123,7 +111,15 @@ class _SpiPhy(BasePhy):
with contextlib.closing(spidev.SpiDev(self.__bus, self.__chip)) as spi: with contextlib.closing(spidev.SpiDev(self.__bus, self.__chip)) as spi:
spi.mode = 0 spi.mode = 0
spi.max_speed_hz = self.__max_freq spi.max_speed_hz = self.__max_freq
yield _SpiPhyConnection(spi, self.__read_timeout, self.__read_delay)
def xfer(data: bytes) -> bytes:
return spi.xfer(data, self.__max_freq, self.__block_usec)
yield _SpiPhyConnection(
xfer=xfer,
read_timeout=self.__read_timeout,
read_delay=self.__read_delay,
)
# ===== # =====
@ -133,23 +129,25 @@ class Plugin(BaseMcuHid):
bus: int, bus: int,
chip: int, chip: int,
max_freq: int, max_freq: int,
block_usec: int,
read_timeout: float, read_timeout: float,
read_delay: float, read_delay: float,
**kwargs: Any, **kwargs: Any,
) -> None: ) -> None:
super().__init__( super().__init__(
phy=_SpiPhy(bus, chip, max_freq, read_timeout, read_delay), phy=_SpiPhy(bus, chip, max_freq, block_usec, read_timeout, read_delay),
**kwargs, **kwargs,
) )
@classmethod @classmethod
def get_plugin_options(cls) -> Dict: def get_plugin_options(cls) -> Dict:
return { return {
"bus": Option(0, type=valid_int_f0), "bus": Option(0, type=valid_int_f0),
"chip": Option(0, type=valid_int_f0), "chip": Option(0, type=valid_int_f0),
"max_freq": Option(1000000, type=valid_int_f1), "max_freq": Option(400000, type=valid_int_f1),
"read_timeout": Option(2.0, type=valid_float_f01), "block_usec": Option(1, type=valid_int_f0),
"read_delay": Option(0.001, type=valid_float_f0), "read_timeout": Option(2.0, type=valid_float_f01),
"read_delay": Option(0.001, type=valid_float_f0),
**BaseMcuHid.get_plugin_options(), **BaseMcuHid.get_plugin_options(),
} }