mirror of
https://github.com/luckfox-eng29/kvm.git
synced 2026-01-18 03:28:19 +01:00
feat: hid rpc channel (#755)
* feat: use hidRpcChannel to save bandwidth * chore: simplify handshake of hid rpc * add logs * chore: add timeout when writing to hid endpoints * fix issues * chore: show hid rpc version * refactor hidrpc marshal / unmarshal * add queues for keyboard / mouse event * chore: change logging level of JSONRPC send event to trace * minor changes related to logging * fix: nil check * chore: add comments and remove unused code * add useMouse * chore: log msg data only when debug or trace mode * chore: make tslint happy * chore: unlock keyboardStateLock before calling onKeysDownChange * chore: remove keyPressReportApiAvailable * chore: change version handle * chore: clean up unused functions * remove comments
This commit is contained in:
162
hidrpc.go
Normal file
162
hidrpc.go
Normal file
@@ -0,0 +1,162 @@
|
||||
package kvm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/jetkvm/kvm/internal/hidrpc"
|
||||
"github.com/jetkvm/kvm/internal/usbgadget"
|
||||
)
|
||||
|
||||
func handleHidRPCMessage(message hidrpc.Message, session *Session) {
|
||||
var rpcErr error
|
||||
|
||||
switch message.Type() {
|
||||
case hidrpc.TypeHandshake:
|
||||
message, err := hidrpc.NewHandshakeMessage().Marshal()
|
||||
if err != nil {
|
||||
logger.Warn().Err(err).Msg("failed to marshal handshake message")
|
||||
return
|
||||
}
|
||||
if err := session.HidChannel.Send(message); err != nil {
|
||||
logger.Warn().Err(err).Msg("failed to send handshake message")
|
||||
return
|
||||
}
|
||||
session.hidRPCAvailable = true
|
||||
case hidrpc.TypeKeypressReport, hidrpc.TypeKeyboardReport:
|
||||
keysDownState, err := handleHidRPCKeyboardInput(message)
|
||||
if keysDownState != nil {
|
||||
session.reportHidRPCKeysDownState(*keysDownState)
|
||||
}
|
||||
rpcErr = err
|
||||
case hidrpc.TypePointerReport:
|
||||
pointerReport, err := message.PointerReport()
|
||||
if err != nil {
|
||||
logger.Warn().Err(err).Msg("failed to get pointer report")
|
||||
return
|
||||
}
|
||||
rpcErr = rpcAbsMouseReport(pointerReport.X, pointerReport.Y, pointerReport.Button)
|
||||
case hidrpc.TypeMouseReport:
|
||||
mouseReport, err := message.MouseReport()
|
||||
if err != nil {
|
||||
logger.Warn().Err(err).Msg("failed to get mouse report")
|
||||
return
|
||||
}
|
||||
rpcErr = rpcRelMouseReport(mouseReport.DX, mouseReport.DY, mouseReport.Button)
|
||||
default:
|
||||
logger.Warn().Uint8("type", uint8(message.Type())).Msg("unknown HID RPC message type")
|
||||
}
|
||||
|
||||
if rpcErr != nil {
|
||||
logger.Warn().Err(rpcErr).Msg("failed to handle HID RPC message")
|
||||
}
|
||||
}
|
||||
|
||||
func onHidMessage(data []byte, session *Session) {
|
||||
scopedLogger := hidRPCLogger.With().Bytes("data", data).Logger()
|
||||
scopedLogger.Debug().Msg("HID RPC message received")
|
||||
|
||||
if len(data) < 1 {
|
||||
scopedLogger.Warn().Int("length", len(data)).Msg("received empty data in HID RPC message handler")
|
||||
return
|
||||
}
|
||||
|
||||
var message hidrpc.Message
|
||||
|
||||
if err := hidrpc.Unmarshal(data, &message); err != nil {
|
||||
scopedLogger.Warn().Err(err).Msg("failed to unmarshal HID RPC message")
|
||||
return
|
||||
}
|
||||
|
||||
scopedLogger = scopedLogger.With().Str("descr", message.String()).Logger()
|
||||
|
||||
t := time.Now()
|
||||
|
||||
r := make(chan interface{})
|
||||
go func() {
|
||||
handleHidRPCMessage(message, session)
|
||||
r <- nil
|
||||
}()
|
||||
select {
|
||||
case <-time.After(1 * time.Second):
|
||||
scopedLogger.Warn().Msg("HID RPC message timed out")
|
||||
case <-r:
|
||||
scopedLogger.Debug().Dur("duration", time.Since(t)).Msg("HID RPC message handled")
|
||||
}
|
||||
}
|
||||
|
||||
func handleHidRPCKeyboardInput(message hidrpc.Message) (*usbgadget.KeysDownState, error) {
|
||||
switch message.Type() {
|
||||
case hidrpc.TypeKeypressReport:
|
||||
keypressReport, err := message.KeypressReport()
|
||||
if err != nil {
|
||||
logger.Warn().Err(err).Msg("failed to get keypress report")
|
||||
return nil, err
|
||||
}
|
||||
keysDownState, rpcError := rpcKeypressReport(keypressReport.Key, keypressReport.Press)
|
||||
return &keysDownState, rpcError
|
||||
case hidrpc.TypeKeyboardReport:
|
||||
keyboardReport, err := message.KeyboardReport()
|
||||
if err != nil {
|
||||
logger.Warn().Err(err).Msg("failed to get keyboard report")
|
||||
return nil, err
|
||||
}
|
||||
keysDownState, rpcError := rpcKeyboardReport(keyboardReport.Modifier, keyboardReport.Keys)
|
||||
return &keysDownState, rpcError
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unknown HID RPC message type: %d", message.Type())
|
||||
}
|
||||
|
||||
func reportHidRPC(params any, session *Session) {
|
||||
if session == nil {
|
||||
logger.Warn().Msg("session is nil, skipping reportHidRPC")
|
||||
return
|
||||
}
|
||||
|
||||
if !session.hidRPCAvailable || session.HidChannel == nil {
|
||||
logger.Warn().Msg("HID RPC is not available, skipping reportHidRPC")
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
message []byte
|
||||
err error
|
||||
)
|
||||
switch params := params.(type) {
|
||||
case usbgadget.KeyboardState:
|
||||
message, err = hidrpc.NewKeyboardLedMessage(params).Marshal()
|
||||
case usbgadget.KeysDownState:
|
||||
message, err = hidrpc.NewKeydownStateMessage(params).Marshal()
|
||||
default:
|
||||
err = fmt.Errorf("unknown HID RPC message type: %T", params)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logger.Warn().Err(err).Msg("failed to marshal HID RPC message")
|
||||
return
|
||||
}
|
||||
|
||||
if message == nil {
|
||||
logger.Warn().Msg("failed to marshal HID RPC message")
|
||||
return
|
||||
}
|
||||
|
||||
if err := session.HidChannel.Send(message); err != nil {
|
||||
logger.Warn().Err(err).Msg("failed to send HID RPC message")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Session) reportHidRPCKeyboardLedState(state usbgadget.KeyboardState) {
|
||||
if !s.hidRPCAvailable {
|
||||
writeJSONRPCEvent("keyboardLedState", state, s)
|
||||
}
|
||||
reportHidRPC(state, s)
|
||||
}
|
||||
|
||||
func (s *Session) reportHidRPCKeysDownState(state usbgadget.KeysDownState) {
|
||||
if !s.hidRPCAvailable {
|
||||
writeJSONRPCEvent("keysDownState", state, s)
|
||||
}
|
||||
reportHidRPC(state, s)
|
||||
}
|
||||
Reference in New Issue
Block a user