summaryrefslogtreecommitdiff
path: root/kvmd
diff options
context:
space:
mode:
authorDevaev Maxim <[email protected]>2020-11-19 23:28:23 +0300
committerDevaev Maxim <[email protected]>2020-11-20 00:15:18 +0300
commita77db72355c760eb38deb4c46a83fb7d3f2ed008 (patch)
tree208cd060c234fbfd77ef3c1879ed906950660bed /kvmd
parent188de715153100806bc4a95f3888f6f03f1ede2f (diff)
multihid firmware
Diffstat (limited to 'kvmd')
-rw-r--r--kvmd/aiomulti.py19
-rw-r--r--kvmd/plugins/hid/_mcu/__init__.py97
-rw-r--r--kvmd/plugins/hid/bt/__init__.py5
-rw-r--r--kvmd/plugins/hid/otg/__init__.py6
-rw-r--r--kvmd/plugins/hid/serial.py5
-rw-r--r--kvmd/plugins/hid/spi.py10
6 files changed, 96 insertions, 46 deletions
diff --git a/kvmd/aiomulti.py b/kvmd/aiomulti.py
index c0fca419..490b787a 100644
--- a/kvmd/aiomulti.py
+++ b/kvmd/aiomulti.py
@@ -25,7 +25,9 @@ import queue
from typing import Tuple
from typing import Dict
+from typing import Type
from typing import TypeVar
+from typing import Generic
from typing import Optional
from . import aiotools
@@ -71,14 +73,19 @@ class AioProcessNotifier:
# =====
-class AioSharedFlags:
+_SharedFlagT = TypeVar("_SharedFlagT", int, bool)
+
+
+class AioSharedFlags(Generic[_SharedFlagT]):
def __init__(
self,
- initial: Dict[str, bool],
+ initial: Dict[str, _SharedFlagT],
notifier: AioProcessNotifier,
+ type: Type[_SharedFlagT]=bool, # pylint: disable=redefined-builtin
) -> None:
self.__notifier = notifier
+ self.__type: Type[_SharedFlagT] = type
self.__flags = {
key: multiprocessing.RawValue("i", int(value)) # type: ignore
@@ -87,7 +94,7 @@ class AioSharedFlags:
self.__lock = multiprocessing.Lock()
- def update(self, **kwargs: bool) -> None:
+ def update(self, **kwargs: _SharedFlagT) -> None:
changed = False
with self.__lock:
for (key, value) in kwargs.items():
@@ -98,12 +105,12 @@ class AioSharedFlags:
if changed:
self.__notifier.notify()
- async def get(self) -> Dict[str, bool]:
+ async def get(self) -> Dict[str, _SharedFlagT]:
return (await aiotools.run_async(self.__inner_get))
- def __inner_get(self) -> Dict[str, bool]:
+ def __inner_get(self) -> Dict[str, _SharedFlagT]:
with self.__lock:
return {
- key: bool(shared.value)
+ key: self.__type(shared.value)
for (key, shared) in self.__flags.items()
}
diff --git a/kvmd/plugins/hid/_mcu/__init__.py b/kvmd/plugins/hid/_mcu/__init__.py
index b46c876d..678b4887 100644
--- a/kvmd/plugins/hid/_mcu/__init__.py
+++ b/kvmd/plugins/hid/_mcu/__init__.py
@@ -137,6 +137,19 @@ class _MouseMoveEvent(_BaseEvent):
@dataclasses.dataclass(frozen=True)
+class _MouseRelativeEvent(_BaseEvent):
+ delta_x: int
+ delta_y: int
+
+ def __post_init__(self) -> None:
+ assert -127 <= self.delta_x <= 127
+ assert -127 <= self.delta_y <= 127
+
+ def make_command(self) -> bytes:
+ return struct.pack(">Bbbxx", 0x15, self.delta_x, self.delta_y)
+
+
[email protected](frozen=True)
class _MouseWheelEvent(_BaseEvent):
delta_x: int
delta_y: int
@@ -196,12 +209,9 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many-
self.__notifier = aiomulti.AioProcessNotifier()
self.__state_flags = aiomulti.AioSharedFlags({
- "keyboard_online": True,
- "mouse_online": True,
- "caps": False,
- "scroll": False,
- "num": False,
- }, self.__notifier)
+ "online": 0,
+ "status": 0,
+ }, self.__notifier, type=int)
self.__stop_event = multiprocessing.Event()
@@ -226,19 +236,51 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many-
async def get_state(self) -> Dict:
state = await self.__state_flags.get()
+ online = bool(state["online"])
+ pong = (state["status"] >> 16) & 0xFF
+ outputs = (state["status"] >> 8) & 0xFF
+ features = state["status"] & 0xFF
+
+ absolute = True
+ if online and (outputs & 0b00111000) in [0b00010000, 0b00011000]:
+ absolute = False
+
+ keyboard_outputs: Dict = {"available": {}, "active": ""}
+ mouse_outputs: Dict = {"available": {}, "active": ""}
+ if outputs & 0b10000000: # Dynamic
+ if features & 0b00000001: # USB
+ keyboard_outputs["available"]["usb"] = {"name": "USB"}
+ mouse_outputs["available"]["usb_abs"] = {"name": "USB", "absolute": True}
+ mouse_outputs["available"]["usb_rel"] = {"name": "USB Relative", "absolute": False}
+ if features & 0b00000010: # PS/2
+ keyboard_outputs["available"]["ps2"] = {"name": "PS/2"}
+ mouse_outputs["available"]["ps2"] = {"name": "PS/2"}
+
+ keyboard_outputs["active"] = {
+ 0b00000001: "usb",
+ 0b00000011: "ps2",
+ }.get(outputs & 0b00000111, "")
+ mouse_outputs["active"] = {
+ 0b00001000: "usb_abs",
+ 0b00010000: "usb_rel",
+ 0b00011000: "ps2",
+ }.get(outputs & 0b00111000, "")
+
return {
- "online": (state["keyboard_online"] and state["mouse_online"]),
+ "online": online,
"keyboard": {
- "online": state["keyboard_online"],
+ "online": (online and not (pong & 0b00001000)),
"leds": {
- "caps": state["caps"],
- "scroll": state["scroll"],
- "num": state["num"],
+ "caps": bool(pong & 0b00000001),
+ "scroll": bool(pong & 0b00000010),
+ "num": bool(pong & 0b00000100),
},
+ "outputs": keyboard_outputs,
},
"mouse": {
- "online": state["mouse_online"],
- "absolute": True,
+ "online": (online and not (pong & 0b00010000)),
+ "absolute": absolute,
+ "outputs": mouse_outputs,
},
}
@@ -287,8 +329,7 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many-
self.__queue_event(_MouseMoveEvent(to_x, to_y))
def send_mouse_relative_event(self, delta_x: int, delta_y: int) -> None:
- _ = delta_x # No relative events yet
- _ = delta_y
+ self.__queue_event(_MouseRelativeEvent(delta_x, delta_y))
def send_mouse_wheel_event(self, delta_x: int, delta_y: int) -> None:
self.__queue_event(_MouseWheelEvent(delta_x, delta_y))
@@ -350,8 +391,8 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many-
read_retries -= 1
raise _TempRequestError(f"No response from HID: request={request!r}")
- assert len(response) == 4, response
- if self.__make_crc16(response[-4:-2]) != struct.unpack(">H", response[-2:])[0]:
+ assert len(response) in (4, 8), response
+ if self.__make_crc16(response[:-2]) != struct.unpack(">H", response[-2:])[0]:
request = self.__make_request(b"\x02\x00\x00\x00\x00") # Repeat an answer
raise _TempRequestError("Invalid response CRC; requesting response again ...")
@@ -368,7 +409,7 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many-
self.__set_state_online(True)
return True
elif code & 0x80: # Pong/Done with state
- self.__set_state_code(code)
+ self.__set_state_pong(response)
return True
raise _TempRequestError(f"Invalid response from HID: request={request!r}; code=0x{code:02X}")
@@ -401,19 +442,13 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many-
return error_retval
def __set_state_online(self, online: bool) -> None:
- self.__state_flags.update(
- keyboard_online=online,
- mouse_online=online,
- )
-
- def __set_state_code(self, code: int) -> None:
- self.__state_flags.update(
- keyboard_online=(not (code & 0b00001000)),
- mouse_online=(not (code & 0b00010000)),
- caps=bool(code & 0b00000001),
- scroll=bool(code & 0b00000010),
- num=bool(code & 0b00000100),
- )
+ self.__state_flags.update(online=int(online))
+
+ def __set_state_pong(self, data: bytes) -> None:
+ status = data[1] << 16
+ if len(data) > 4:
+ status |= (data[2] << 8) | data[3]
+ self.__state_flags.update(online=1, status=status)
def __send_request(self, conn: BasePhyConnection, request: bytes) -> bytes:
if not self.__noop:
diff --git a/kvmd/plugins/hid/bt/__init__.py b/kvmd/plugins/hid/bt/__init__.py
index 6c8dc46b..2d9ce860 100644
--- a/kvmd/plugins/hid/bt/__init__.py
+++ b/kvmd/plugins/hid/bt/__init__.py
@@ -131,8 +131,9 @@ class Plugin(BaseHid): # pylint: disable=too-many-instance-attributes
async def get_state(self) -> Dict:
state = await self.__server.get_state()
+ outputs: Dict = {"available": {}, "active": ""}
return {
- "online": state["online"],
+ "online": True,
"keyboard": {
"online": state["online"],
"leds": {
@@ -140,10 +141,12 @@ class Plugin(BaseHid): # pylint: disable=too-many-instance-attributes
"scroll": state["scroll"],
"num": state["num"],
},
+ "outputs": outputs,
},
"mouse": {
"online": state["online"],
"absolute": False,
+ "outputs": outputs,
},
}
diff --git a/kvmd/plugins/hid/otg/__init__.py b/kvmd/plugins/hid/otg/__init__.py
index aa3d79b2..e685226b 100644
--- a/kvmd/plugins/hid/otg/__init__.py
+++ b/kvmd/plugins/hid/otg/__init__.py
@@ -89,8 +89,9 @@ class Plugin(BaseHid):
async def get_state(self) -> Dict:
keyboard_state = await self.__keyboard_proc.get_state()
mouse_state = await self.__mouse_proc.get_state()
+ outputs: Dict = {"available": {}, "active": ""}
return {
- "online": (keyboard_state["online"] and mouse_state["online"]),
+ "online": True,
"keyboard": {
"online": keyboard_state["online"],
"leds": {
@@ -98,8 +99,9 @@ class Plugin(BaseHid):
"scroll": keyboard_state["scroll"],
"num": keyboard_state["num"],
},
+ "outputs": outputs,
},
- "mouse": mouse_state,
+ "mouse": {**mouse_state, "outputs": outputs},
}
async def poll_state(self) -> AsyncGenerator[Dict, None]:
diff --git a/kvmd/plugins/hid/serial.py b/kvmd/plugins/hid/serial.py
index dee992b6..da61f2c6 100644
--- a/kvmd/plugins/hid/serial.py
+++ b/kvmd/plugins/hid/serial.py
@@ -51,7 +51,10 @@ class _SerialPhyConnection(BasePhyConnection):
if self.__tty.in_waiting:
self.__tty.read_all()
assert self.__tty.write(request) == 8
- return self.__tty.read(4)
+ data = self.__tty.read(4)
+ if data[0] == 0x34: # New response protocol
+ data += self.__tty.read(4)
+ return data
class _SerialPhy(BasePhy):
diff --git a/kvmd/plugins/hid/spi.py b/kvmd/plugins/hid/spi.py
index 7dadac0e..785ca779 100644
--- a/kvmd/plugins/hid/spi.py
+++ b/kvmd/plugins/hid/spi.py
@@ -67,7 +67,7 @@ class _SpiPhyConnection(BasePhyConnection):
assert request[0] == 0x33
deadline_ts = time.monotonic() + self.__read_timeout
- dummy = b"\x00" * 8
+ dummy = b"\x00" * 10
while time.monotonic() < deadline_ts:
if bytes(self.__xfer(dummy)) == dummy:
break
@@ -81,15 +81,15 @@ class _SpiPhyConnection(BasePhyConnection):
deadline_ts = time.monotonic() + self.__read_timeout
found = False
while time.monotonic() < deadline_ts:
- for byte in self.__xfer(b"\x00" * (5 - len(response))):
+ for byte in self.__xfer(b"\x00" * (9 - len(response))):
if not found:
- if byte != 0x33:
+ if byte == 0:
continue
found = True
response.append(byte)
- if len(response) == 4:
+ if len(response) == 8:
break
- if len(response) == 4:
+ if len(response) == 8:
break
else:
get_logger(0).error("SPI timeout reached while responce waiting")