moved arduino hid to hid/arduino

This commit is contained in:
Maxim Devaev
2023-07-31 02:17:23 +03:00
parent cf44668af9
commit 1a8f98a64f
50 changed files with 5 additions and 6 deletions

View File

@@ -0,0 +1,40 @@
/*****************************************************************************
# #
# 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 <avr/eeprom.h>
#include "storage.h"
namespace DRIVERS {
struct Eeprom : public Storage {
using Storage::Storage;
void readBlock(void *dest, const void *src, size_t size) override {
eeprom_read_block(dest, src, size);
}
void updateBlock(const void *src, void *dest, size_t size) override {
eeprom_update_block(src, dest, size);
}
};
}

View File

@@ -0,0 +1,94 @@
/*****************************************************************************
# #
# 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 "usb/hid.h"
#include "ps2/hid.h"
#include "factory.h"
#include "eeprom.h"
#include "serial.h"
#include "spi.h"
#ifndef ARDUINO_ARCH_AVR
# error "Only AVR is supported"
#endif
namespace DRIVERS {
Keyboard *Factory::makeKeyboard(type _type) {
switch (_type) {
# ifdef HID_WITH_USB
case USB_KEYBOARD:
return new UsbKeyboard();
# endif
# ifdef HID_WITH_PS2
case PS2_KEYBOARD:
return new Ps2Keyboard();
# endif
default:
return new Keyboard(DUMMY);
}
}
Mouse *Factory::makeMouse(type _type) {
switch (_type) {
# ifdef HID_WITH_USB
case USB_MOUSE_ABSOLUTE:
case USB_MOUSE_ABSOLUTE_WIN98:
return new UsbMouseAbsolute(_type);
case USB_MOUSE_RELATIVE:
return new UsbMouseRelative();
# endif
default:
return new Mouse(DRIVERS::DUMMY);
}
}
Storage *Factory::makeStorage(type _type) {
switch (_type) {
# ifdef HID_DYNAMIC
case NON_VOLATILE_STORAGE:
return new Eeprom(DRIVERS::NON_VOLATILE_STORAGE);
# endif
default:
return new Storage(DRIVERS::DUMMY);
}
}
Board *Factory::makeBoard(type _type) {
switch (_type) {
default:
return new Board(DRIVERS::DUMMY);
}
}
Connection *Factory::makeConnection(type _type) {
# ifdef CMD_SERIAL
return new Serial();
# elif defined(CMD_SPI)
return new Spi();
# else
# error CMD phy is not defined
# endif
}
}

View File

@@ -0,0 +1,95 @@
/*****************************************************************************
# #
# 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 <Arduino.h>
#include <ps2dev.h>
#include "keyboard.h"
#include "keymap.h"
// #define HID_PS2_KBD_CLOCK_PIN 7
// #define HID_PS2_KBD_DATA_PIN 5
class Ps2Keyboard : public DRIVERS::Keyboard {
// https://wiki.osdev.org/PS/2_Keyboard
public:
Ps2Keyboard() : DRIVERS::Keyboard(DRIVERS::PS2_KEYBOARD), _dev(HID_PS2_KBD_CLOCK_PIN, HID_PS2_KBD_DATA_PIN) {}
void begin() override {
_dev.keyboard_init();
}
void periodic() override {
_dev.keyboard_handle(&_leds);
}
void sendKey(uint8_t code, bool state) override {
Ps2KeyType ps2_type;
uint8_t ps2_code;
keymapPs2(code, &ps2_type, &ps2_code);
if (ps2_type != PS2_KEY_TYPE_UNKNOWN) {
// Не отправлялась часть нажатий. Когда clock на нуле, комп не принимает ничего от клавы.
// Этот костыль понижает процент пропущенных нажатий.
while (digitalRead(HID_PS2_KBD_CLOCK_PIN) == 0) {};
if (state) {
switch (ps2_type) {
case PS2_KEY_TYPE_REG: _dev.keyboard_press(ps2_code); break;
case PS2_KEY_TYPE_SPEC: _dev.keyboard_press_special(ps2_code); break;
case PS2_KEY_TYPE_PRINT: _dev.keyboard_press_printscreen(); break;
case PS2_KEY_TYPE_PAUSE: _dev.keyboard_pausebreak(); break;
case PS2_KEY_TYPE_UNKNOWN: break;
}
} else {
switch (ps2_type) {
case PS2_KEY_TYPE_REG: _dev.keyboard_release(ps2_code); break;
case PS2_KEY_TYPE_SPEC: _dev.keyboard_release_special(ps2_code); break;
case PS2_KEY_TYPE_PRINT: _dev.keyboard_release_printscreen(); break;
case PS2_KEY_TYPE_PAUSE: break;
case PS2_KEY_TYPE_UNKNOWN: break;
}
}
}
}
bool isOffline() override {
return false;
}
KeyboardLedsState getLeds() override {
periodic();
KeyboardLedsState result = {
.caps = _leds & 0b00000100,
.scroll = _leds & 0b00000001,
.num = _leds & 0b00000010,
};
return result;
}
private:
PS2dev _dev;
uint8_t _leds = 0;
};

View File

@@ -0,0 +1,152 @@
/*****************************************************************************
# #
# 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
enum Ps2KeyType : uint8_t {
PS2_KEY_TYPE_UNKNOWN = 0,
PS2_KEY_TYPE_REG = 1,
PS2_KEY_TYPE_SPEC = 2,
PS2_KEY_TYPE_PRINT = 3,
PS2_KEY_TYPE_PAUSE = 4,
};
void keymapPs2(uint8_t code, Ps2KeyType *ps2_type, uint8_t *ps2_code) {
*ps2_type = PS2_KEY_TYPE_UNKNOWN;
*ps2_code = 0;
switch (code) {
case 1: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 28; return; // KeyA
case 2: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 50; return; // KeyB
case 3: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 33; return; // KeyC
case 4: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 35; return; // KeyD
case 5: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 36; return; // KeyE
case 6: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 43; return; // KeyF
case 7: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 52; return; // KeyG
case 8: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 51; return; // KeyH
case 9: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 67; return; // KeyI
case 10: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 59; return; // KeyJ
case 11: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 66; return; // KeyK
case 12: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 75; return; // KeyL
case 13: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 58; return; // KeyM
case 14: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 49; return; // KeyN
case 15: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 68; return; // KeyO
case 16: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 77; return; // KeyP
case 17: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 21; return; // KeyQ
case 18: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 45; return; // KeyR
case 19: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 27; return; // KeyS
case 20: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 44; return; // KeyT
case 21: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 60; return; // KeyU
case 22: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 42; return; // KeyV
case 23: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 29; return; // KeyW
case 24: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 34; return; // KeyX
case 25: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 53; return; // KeyY
case 26: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 26; return; // KeyZ
case 27: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 22; return; // Digit1
case 28: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 30; return; // Digit2
case 29: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 38; return; // Digit3
case 30: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 37; return; // Digit4
case 31: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 46; return; // Digit5
case 32: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 54; return; // Digit6
case 33: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 61; return; // Digit7
case 34: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 62; return; // Digit8
case 35: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 70; return; // Digit9
case 36: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 69; return; // Digit0
case 37: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 90; return; // Enter
case 38: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 118; return; // Escape
case 39: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 102; return; // Backspace
case 40: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 13; return; // Tab
case 41: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 41; return; // Space
case 42: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 78; return; // Minus
case 43: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 85; return; // Equal
case 44: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 84; return; // BracketLeft
case 45: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 91; return; // BracketRight
case 46: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 93; return; // Backslash
case 47: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 76; return; // Semicolon
case 48: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 82; return; // Quote
case 49: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 14; return; // Backquote
case 50: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 65; return; // Comma
case 51: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 73; return; // Period
case 52: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 74; return; // Slash
case 53: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 88; return; // CapsLock
case 54: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 5; return; // F1
case 55: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 6; return; // F2
case 56: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 4; return; // F3
case 57: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 12; return; // F4
case 58: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 3; return; // F5
case 59: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 11; return; // F6
case 60: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 131; return; // F7
case 61: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 10; return; // F8
case 62: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 1; return; // F9
case 63: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 9; return; // F10
case 64: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 120; return; // F11
case 65: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 7; return; // F12
case 66: *ps2_type = PS2_KEY_TYPE_PRINT; *ps2_code = 255; return; // PrintScreen
case 67: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 112; return; // Insert
case 68: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 108; return; // Home
case 69: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 125; return; // PageUp
case 70: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 113; return; // Delete
case 71: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 105; return; // End
case 72: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 122; return; // PageDown
case 73: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 116; return; // ArrowRight
case 74: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 107; return; // ArrowLeft
case 75: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 114; return; // ArrowDown
case 76: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 117; return; // ArrowUp
case 77: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 20; return; // ControlLeft
case 78: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 18; return; // ShiftLeft
case 79: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 17; return; // AltLeft
case 80: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 31; return; // MetaLeft
case 81: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 20; return; // ControlRight
case 82: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 89; return; // ShiftRight
case 83: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 17; return; // AltRight
case 84: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 39; return; // MetaRight
case 85: *ps2_type = PS2_KEY_TYPE_PAUSE; *ps2_code = 255; return; // Pause
case 86: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 126; return; // ScrollLock
case 87: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 119; return; // NumLock
case 88: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 47; return; // ContextMenu
case 89: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 74; return; // NumpadDivide
case 90: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 124; return; // NumpadMultiply
case 91: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 123; return; // NumpadSubtract
case 92: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 121; return; // NumpadAdd
case 93: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 90; return; // NumpadEnter
case 94: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 105; return; // Numpad1
case 95: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 114; return; // Numpad2
case 96: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 122; return; // Numpad3
case 97: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 107; return; // Numpad4
case 98: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 115; return; // Numpad5
case 99: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 116; return; // Numpad6
case 100: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 108; return; // Numpad7
case 101: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 117; return; // Numpad8
case 102: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 125; return; // Numpad9
case 103: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 112; return; // Numpad0
case 104: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 113; return; // NumpadDecimal
case 105: *ps2_type = PS2_KEY_TYPE_SPEC; *ps2_code = 94; return; // Power
case 106: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 97; return; // IntlBackslash
case 107: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 106; return; // IntlYen
case 108: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 81; return; // IntlRo
case 109: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 19; return; // KanaMode
case 110: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 100; return; // Convert
case 111: *ps2_type = PS2_KEY_TYPE_REG; *ps2_code = 103; return; // NonConvert
}
}

View File

@@ -0,0 +1,44 @@
/*****************************************************************************
# #
# 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
enum Ps2KeyType : uint8_t {
PS2_KEY_TYPE_UNKNOWN = 0,
PS2_KEY_TYPE_REG = 1,
PS2_KEY_TYPE_SPEC = 2,
PS2_KEY_TYPE_PRINT = 3,
PS2_KEY_TYPE_PAUSE = 4,
};
<%! import operator %>
void keymapPs2(uint8_t code, Ps2KeyType *ps2_type, uint8_t *ps2_code) {
*ps2_type = PS2_KEY_TYPE_UNKNOWN;
*ps2_code = 0;
switch (code) {
% for km in sorted(keymap, key=operator.attrgetter("mcu_code")):
case ${km.mcu_code}: *ps2_type = PS2_KEY_TYPE_${km.ps2_key.type.upper()}; *ps2_code = ${km.ps2_key.code}; return; // ${km.web_name}
% endfor
}
}

View File

@@ -0,0 +1,83 @@
/*****************************************************************************
# #
# 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 "spi.h"
#ifdef CMD_SPI
static volatile uint8_t _spi_in[8] = {0};
static volatile uint8_t _spi_in_index = 0;
static volatile uint8_t _spi_out[8] = {0};
static volatile uint8_t _spi_out_index = 0;
namespace DRIVERS {
void Spi::begin() {
pinMode(MISO, OUTPUT);
SPCR = (1 << SPE) | (1 << SPIE); // Slave, SPI En, IRQ En
}
void Spi::periodic() {
if (!_spi_out[0] && _spi_in_index == 8) {
_data_cb((const uint8_t *)_spi_in, 8);
}
}
void Spi::write(const uint8_t *data, size_t size) {
// Меджик в нулевом байте разрешает начать ответ
for (int index = 7; index >= 0; --index) {
_spi_out[index] = data[index];
}
}
}
ISR(SPI_STC_vect) {
uint8_t in = SPDR;
if (_spi_out[0] && _spi_out_index < 8) {
SPDR = _spi_out[_spi_out_index];
if (!(SPSR & (1 << WCOL))) {
++_spi_out_index;
if (_spi_out_index == 8) {
_spi_out_index = 0;
_spi_in_index = 0;
_spi_out[0] = 0;
}
}
} else {
static bool receiving = false;
if (!receiving && in != 0) {
receiving = true;
}
if (receiving && _spi_in_index < 8) {
_spi_in[_spi_in_index] = in;
++_spi_in_index;
}
if (_spi_in_index == 8) {
receiving = false;
}
SPDR = 0;
}
}
#endif

View File

@@ -0,0 +1,40 @@
/*****************************************************************************
# #
# 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 <Arduino.h>
#include "connection.h"
namespace DRIVERS {
struct Spi : public Connection {
Spi() : Connection(CONNECTION) {}
void begin() override;
void periodic() override;
void write(const uint8_t *data, size_t size) override;
};
}

View File

@@ -0,0 +1,230 @@
/*****************************************************************************
# #
# 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 <Arduino.h>
#include <HID-Project.h>
#include "keyboard.h"
#include "mouse.h"
#include "tools.h"
#include "usb-keymap.h"
#ifdef AUM
# include "aum.h"
#endif
using namespace DRIVERS;
// -----------------------------------------------------------------------------
#ifdef HID_USB_CHECK_ENDPOINT
// https://github.com/arduino/ArduinoCore-avr/blob/2f67c916f6ab6193c404eebe22efe901e0f9542d/cores/arduino/USBCore.cpp#L249
// https://sourceforge.net/p/arduinomidilib/svn/41/tree/branch/3.1/Teensy/teensy_core/usb_midi/usb_api.cpp#l103
# ifdef AUM
# define CHECK_AUM_USB { if (!aumIsUsbConnected()) { return true; } }
# else
# define CHECK_AUM_USB
# endif
# define CLS_IS_OFFLINE(_hid) \
bool isOffline() override { \
CHECK_AUM_USB; \
uint8_t ep = _hid.getPluggedEndpoint(); \
uint8_t intr_state = SREG; \
cli(); \
UENUM = ep & 7; \
bool rw_allowed = UEINTX & (1 << RWAL); \
SREG = intr_state; \
if (rw_allowed) { \
return false; \
} \
return true; \
}
# define CHECK_HID_EP { if (isOffline()) return; }
#else
# define CLS_IS_OFFLINE(_hid) \
bool isOffline() override { \
return false; \
}
# define CHECK_HID_EP
#endif
class UsbKeyboard : public DRIVERS::Keyboard {
public:
UsbKeyboard() : DRIVERS::Keyboard(DRIVERS::USB_KEYBOARD) {}
void begin() override {
_kbd.begin();
}
void periodic() override {
# ifdef HID_USB_CHECK_ENDPOINT
static unsigned long prev_ts = 0;
if (is_micros_timed_out(prev_ts, 50000)) {
static bool prev_online = true;
bool online = !isOffline();
if (!_sent || (online && !prev_online)) {
_sendCurrent();
}
prev_online = online;
prev_ts = micros();
}
# endif
}
void clear() override {
_kbd.releaseAll();
}
void sendKey(uint8_t code, bool state) override {
enum KeyboardKeycode usb_code = keymapUsb(code);
if (usb_code > 0) {
if (state ? _kbd.add(usb_code) : _kbd.remove(usb_code)) {
_sendCurrent();
}
}
}
CLS_IS_OFFLINE(_kbd)
KeyboardLedsState getLeds() override {
uint8_t leds = _kbd.getLeds();
KeyboardLedsState result = {
.caps = leds & LED_CAPS_LOCK,
.scroll = leds & LED_SCROLL_LOCK,
.num = leds & LED_NUM_LOCK,
};
return result;
}
private:
BootKeyboard_ _kbd;
bool _sent = true;
void _sendCurrent() {
# ifdef HID_USB_CHECK_ENDPOINT
if (isOffline()) {
_sent = false;
} else {
# endif
_sent = (_kbd.send() >= 0);
# ifdef HID_USB_CHECK_ENDPOINT
}
# endif
}
};
#define CLS_SEND_BUTTONS \
void sendButtons( \
bool left_select, bool left_state, \
bool right_select, bool right_state, \
bool middle_select, bool middle_state, \
bool up_select, bool up_state, \
bool down_select, bool down_state \
) override { \
if (left_select) _sendButton(MOUSE_LEFT, left_state); \
if (right_select) _sendButton(MOUSE_RIGHT, right_state); \
if (middle_select) _sendButton(MOUSE_MIDDLE, middle_state); \
if (up_select) _sendButton(MOUSE_PREV, up_state); \
if (down_select) _sendButton(MOUSE_NEXT, down_state); \
}
class UsbMouseAbsolute : public DRIVERS::Mouse {
public:
UsbMouseAbsolute(DRIVERS::type _type) : Mouse(_type) {}
void begin() override {
_mouse.begin();
_mouse.setWin98FixEnabled(getType() == DRIVERS::USB_MOUSE_ABSOLUTE_WIN98);
}
void clear() override {
_mouse.releaseAll();
}
CLS_SEND_BUTTONS
void sendMove(int x, int y) override {
CHECK_HID_EP;
_mouse.moveTo(x, y);
}
void sendWheel(int delta_y) override {
// delta_x is not supported by hid-project now
CHECK_HID_EP;
_mouse.move(0, 0, delta_y);
}
CLS_IS_OFFLINE(_mouse)
private:
SingleAbsoluteMouse_ _mouse;
void _sendButton(uint8_t button, bool state) {
CHECK_HID_EP;
if (state) _mouse.press(button);
else _mouse.release(button);
}
};
class UsbMouseRelative : public DRIVERS::Mouse {
public:
UsbMouseRelative() : DRIVERS::Mouse(DRIVERS::USB_MOUSE_RELATIVE) {}
void begin() override {
_mouse.begin();
}
void clear() override {
_mouse.releaseAll();
}
CLS_SEND_BUTTONS
void sendRelative(int x, int y) override {
CHECK_HID_EP;
_mouse.move(x, y, 0);
}
void sendWheel(int delta_y) override {
// delta_x is not supported by hid-project now
CHECK_HID_EP;
_mouse.move(0, 0, delta_y);
}
CLS_IS_OFFLINE(_mouse)
private:
BootMouse_ _mouse;
void _sendButton(uint8_t button, bool state) {
CHECK_HID_EP;
if (state) _mouse.press(button);
else _mouse.release(button);
}
};
#undef CLS_SEND_BUTTONS
#undef CLS_IS_OFFLINE
#undef CHECK_HID_EP