diff options
author | Devaev Maxim <[email protected]> | 2020-11-19 23:28:23 +0300 |
---|---|---|
committer | Devaev Maxim <[email protected]> | 2020-11-20 00:15:18 +0300 |
commit | a77db72355c760eb38deb4c46a83fb7d3f2ed008 (patch) | |
tree | 208cd060c234fbfd77ef3c1879ed906950660bed /kvmd | |
parent | 188de715153100806bc4a95f3888f6f03f1ede2f (diff) |
multihid firmware
Diffstat (limited to 'kvmd')
-rw-r--r-- | kvmd/aiomulti.py | 19 | ||||
-rw-r--r-- | kvmd/plugins/hid/_mcu/__init__.py | 97 | ||||
-rw-r--r-- | kvmd/plugins/hid/bt/__init__.py | 5 | ||||
-rw-r--r-- | kvmd/plugins/hid/otg/__init__.py | 6 | ||||
-rw-r--r-- | kvmd/plugins/hid/serial.py | 5 | ||||
-rw-r--r-- | kvmd/plugins/hid/spi.py | 10 |
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") |