diff options
author | Devaev Maxim <[email protected]> | 2020-11-23 05:01:33 +0300 |
---|---|---|
committer | Devaev Maxim <[email protected]> | 2020-11-23 05:01:33 +0300 |
commit | d58f0847d558ccc9b143bb11217c33f6c6de6913 (patch) | |
tree | 2207f662cbda891f8a876af236454b9509857da8 | |
parent | 31ca16a4f492e6b385363c7175e9daaa3b6805ec (diff) |
auto-reset hid
-rw-r--r-- | kvmd/plugins/hid/_mcu/__init__.py | 79 | ||||
-rw-r--r-- | kvmd/plugins/hid/_mcu/gpio.py | 38 |
2 files changed, 70 insertions, 47 deletions
diff --git a/kvmd/plugins/hid/_mcu/__init__.py b/kvmd/plugins/hid/_mcu/__init__.py index 6e0f902e..52d9912d 100644 --- a/kvmd/plugins/hid/_mcu/__init__.py +++ b/kvmd/plugins/hid/_mcu/__init__.py @@ -55,6 +55,7 @@ from .gpio import Gpio from .proto import REQUEST_PING from .proto import REQUEST_REPEAT from .proto import RESPONSE_LEGACY_OK + from .proto import BaseEvent from .proto import SetKeyboardOutputEvent from .proto import SetMouseOutputEvent @@ -64,6 +65,7 @@ from .proto import MouseButtonEvent from .proto import MouseMoveEvent from .proto import MouseRelativeEvent from .proto import MouseWheelEvent + from .proto import get_active_keyboard from .proto import get_active_mouse from .proto import check_response @@ -85,6 +87,12 @@ class _TempRequestError(_RequestError): # ===== +class _HardResetEvent(BaseEvent): + def make_request(self) -> bytes: + raise RuntimeError("Don't call me") + + +# ===== class BasePhyConnection: def send(self, request: bytes) -> bytes: raise NotImplementedError @@ -151,7 +159,6 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many- } def sysprep(self) -> None: - self.__gpio.open() get_logger(0).info("Starting HID daemon ...") self.start() @@ -214,28 +221,16 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many- prev_state = state await self.__notifier.wait() - @aiotools.atomic async def reset(self) -> None: - await self.__gpio.reset() + self.__queue_event(_HardResetEvent(), clear=True) @aiotools.atomic async def cleanup(self) -> None: - logger = get_logger(0) - try: - if self.is_alive(): - logger.info("Stopping HID daemon ...") - self.__stop_event.set() - if self.exitcode is not None: - self.join() - if self.__phy.has_device(): - get_logger().info("Clearing HID events ...") - try: - with self.__phy.connected() as conn: - self.__process_request(conn, ClearEvent().make_request()) - except Exception: - logger.exception("Can't clear HID events") - finally: - self.__gpio.close() + if self.is_alive(): + get_logger(0).info("Stopping HID daemon ...") + self.__stop_event.set() + if self.exitcode is not None: + self.join() # ===== @@ -282,22 +277,42 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many- while not self.__stop_event.is_set(): try: - if self.__phy.has_device(): - with self.__phy.connected() as conn: - while not (self.__stop_event.is_set() and self.__events_queue.qsize() == 0): - try: - event = self.__events_queue.get(timeout=0.1) - except queue.Empty: - self.__process_request(conn, REQUEST_PING) - else: - if not self.__process_request(conn, event.make_request()): - self.clear_events() - else: + with self.__gpio: + self.__hid_loop() + if self.__phy.has_device(): + logger.info("Clearing HID events ...") + try: + with self.__phy.connected() as conn: + self.__process_request(conn, ClearEvent().make_request()) + except Exception: + logger.exception("Can't clear HID events") + except Exception: + logger.exception("Unexpected error in the GPIO loop") + time.sleep(1) + + def __hid_loop(self) -> None: + logger = get_logger(0) + while not self.__stop_event.is_set(): + try: + if not self.__phy.has_device(): logger.error("Missing HID device") time.sleep(1) + continue + + with self.__phy.connected() as conn: + while not (self.__stop_event.is_set() and self.__events_queue.qsize() == 0): + try: + event = self.__events_queue.get(timeout=0.1) + except queue.Empty: + self.__process_request(conn, REQUEST_PING) + else: + if isinstance(event, _HardResetEvent): + self.__gpio.reset() + elif not self.__process_request(conn, event.make_request()): + self.clear_events() except Exception: self.clear_events() - logger.exception("Unexpected HID error") + logger.exception("Unexpected error in the HID loop") time.sleep(1) def __process_request(self, conn: BasePhyConnection, request: bytes) -> bool: # pylint: disable=too-many-branches @@ -373,3 +388,5 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many- if len(response) > 4: status |= (response[2] << 8) | response[3] self.__state_flags.update(online=1, status=status) + if response[1] & 0b01000000: # Reset required + self.__gpio.reset() diff --git a/kvmd/plugins/hid/_mcu/gpio.py b/kvmd/plugins/hid/_mcu/gpio.py index 40031e33..0fb4dfd0 100644 --- a/kvmd/plugins/hid/_mcu/gpio.py +++ b/kvmd/plugins/hid/_mcu/gpio.py @@ -20,6 +20,10 @@ # ========================================================================== # +import types +import time + +from typing import Type from typing import Optional import gpiod @@ -27,8 +31,6 @@ import gpiod from ....logging import get_logger from .... import env -from .... import aiotools -from .... import aiogp # ===== @@ -46,9 +48,8 @@ class Gpio: self.__chip: Optional[gpiod.Chip] = None self.__reset_line: Optional[gpiod.Line] = None - self.__reset_wip = False - def open(self) -> None: + def __enter__(self) -> None: if self.__reset_pin >= 0: assert self.__chip is None assert self.__reset_line is None @@ -56,23 +57,28 @@ class Gpio: self.__reset_line = self.__chip.get_line(self.__reset_pin) self.__reset_line.request("kvmd::hid::reset", gpiod.LINE_REQ_DIR_OUT, default_vals=[int(self.__reset_inverted)]) - def close(self) -> None: + def __exit__( + self, + _exc_type: Type[BaseException], + _exc: BaseException, + _tb: types.TracebackType, + ) -> None: + if self.__chip: try: self.__chip.close() except Exception: pass + self.__reset_line = None + self.__chip = None - @aiotools.atomic - async def reset(self) -> None: + def reset(self) -> None: if self.__reset_pin >= 0: assert self.__reset_line - if not self.__reset_wip: - self.__reset_wip = True - try: - await aiogp.pulse(self.__reset_line, self.__reset_delay, 1, self.__reset_inverted) - finally: - self.__reset_wip = False - get_logger(0).info("Reset HID performed") - else: - get_logger(0).info("Another reset HID in progress") + try: + self.__reset_line.set_value(int(not self.__reset_inverted)) + time.sleep(self.__reset_delay) + finally: + self.__reset_line.set_value(int(self.__reset_inverted)) + time.sleep(1) + get_logger(0).info("Reset HID performed") |