diff options
author | Devaev Maxim <[email protected]> | 2020-12-26 04:55:40 +0300 |
---|---|---|
committer | Devaev Maxim <[email protected]> | 2020-12-26 04:55:40 +0300 |
commit | 570dd04e29f5c597cf1fdcf9eca8f9f9877d243d (patch) | |
tree | 3f0f495728ac110a80c1232aa6e79300473aeb14 /kvmd | |
parent | 77771bea55dad3c22694438e584b79d2ce6f7d48 (diff) |
otgbind pseudo-gpio plugin
Diffstat (limited to 'kvmd')
-rw-r--r-- | kvmd/apps/__init__.py | 3 | ||||
-rw-r--r-- | kvmd/apps/kvmd/__init__.py | 2 | ||||
-rw-r--r-- | kvmd/apps/kvmd/ugpio.py | 3 | ||||
-rw-r--r-- | kvmd/apps/otg/__init__.py | 7 | ||||
-rw-r--r-- | kvmd/plugins/ugpio/otgbind.py | 119 |
5 files changed, 130 insertions, 4 deletions
diff --git a/kvmd/apps/__init__.py b/kvmd/apps/__init__.py index 008f9e08..7e4f6a81 100644 --- a/kvmd/apps/__init__.py +++ b/kvmd/apps/__init__.py @@ -409,9 +409,10 @@ def _get_config_scheme() -> Dict: "udc": Option("", type=valid_stripped_string), "init_delay": Option(3.0, type=valid_float_f01), + "user": Option("kvmd", type=valid_user), + "devices": { "msd": { - "user": Option("kvmd", type=valid_user), "default": { "stall": Option(False, type=valid_bool), "cdrom": Option(True, type=valid_bool), diff --git a/kvmd/apps/kvmd/__init__.py b/kvmd/apps/kvmd/__init__.py index d11512b1..ac11945f 100644 --- a/kvmd/apps/kvmd/__init__.py +++ b/kvmd/apps/kvmd/__init__.py @@ -81,7 +81,7 @@ def main(argv: Optional[List[str]]=None) -> None: info_manager=InfoManager(global_config), log_reader=LogReader(), wol=WakeOnLan(**config.wol._unpack()), - user_gpio=UserGpio(config.gpio), + user_gpio=UserGpio(config.gpio, global_config.otg.udc), hid=hid, atx=get_atx_class(config.atx.type)(**config.atx._unpack(ignore=["type"])), diff --git a/kvmd/apps/kvmd/ugpio.py b/kvmd/apps/kvmd/ugpio.py index d4e9d0af..b3e01cb3 100644 --- a/kvmd/apps/kvmd/ugpio.py +++ b/kvmd/apps/kvmd/ugpio.py @@ -231,7 +231,7 @@ class _GpioOutput: # pylint: disable=too-many-instance-attributes # ===== class UserGpio: - def __init__(self, config: Section) -> None: + def __init__(self, config: Section, udc: str) -> None: self.__view = config.view self.__notifier = aiotools.AioNotifier() @@ -241,6 +241,7 @@ class UserGpio: instance_name=driver, notifier=self.__notifier, **drv_config._unpack(ignore=["instance_name", "notifier", "type"]), + **({"udc": udc} if drv_config.type == "otgbind" else {}), # Hack ) for (driver, drv_config) in tools.sorted_kvs(config.drivers) } diff --git a/kvmd/apps/otg/__init__.py b/kvmd/apps/otg/__init__.py index 2750549a..b8f93c6d 100644 --- a/kvmd/apps/otg/__init__.py +++ b/kvmd/apps/otg/__init__.py @@ -210,7 +210,7 @@ def _cmd_start(config: Section) -> None: if config.kvmd.msd.type == "otg": logger.info("===== Required MSD =====") - _create_msd(gadget_path, config_path, 0, config.otg.devices.msd.user, **config.otg.devices.msd.default._unpack()) + _create_msd(gadget_path, config_path, 0, config.otg.user, **config.otg.devices.msd.default._unpack()) if config.otg.devices.drives.enabled: logger.info("===== Required MSD extra drives: %d =====", config.otg.devices.drives.count) for instance in range(config.otg.devices.drives.count): @@ -222,6 +222,11 @@ def _cmd_start(config: Section) -> None: _write(join(gadget_path, "UDC"), udc) time.sleep(config.otg.init_delay) + logger.info("Setting DWC2 bind permissions ...") + driver_path = f"{env.SYSFS_PREFIX}/sys/bus/platform/drivers/dwc2" + _chown(join(driver_path, "bind"), config.otg.user) + _chown(join(driver_path, "unbind"), config.otg.user) + logger.info("Ready to work") diff --git a/kvmd/plugins/ugpio/otgbind.py b/kvmd/plugins/ugpio/otgbind.py new file mode 100644 index 00000000..5b5291fa --- /dev/null +++ b/kvmd/plugins/ugpio/otgbind.py @@ -0,0 +1,119 @@ +# ========================================================================== # +# # +# KVMD - The main Pi-KVM daemon. # +# # +# Copyright (C) 2018 Maxim Devaev <[email protected]> # +# # +# 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/>. # +# # +# ========================================================================== # + + +import os +import asyncio + +from typing import Optional + +from ...logging import get_logger + +from ...inotify import InotifyMask +from ...inotify import Inotify + +from ... import env +from ... import aiotools + +from . import BaseUserGpioDriver + + +# ===== +class Plugin(BaseUserGpioDriver): + def __init__( + self, + instance_name: str, + notifier: aiotools.AioNotifier, + + udc: str, # XXX: Not from options, see /kvmd/apps/kvmd/__init__.py for details + ) -> None: + + super().__init__(instance_name, notifier) + + self.__udc = udc + + def register_input(self, pin: int, debounce: float) -> None: + _ = pin + _ = debounce + + def register_output(self, pin: int, initial: Optional[bool]) -> None: + _ = pin + _ = initial + + def prepare(self) -> None: + candidates = sorted(os.listdir(f"{env.SYSFS_PREFIX}/sys/class/udc")) + if not self.__udc: + if len(candidates) == 0: + raise RuntimeError("Can't find any UDC") + self.__udc = candidates[0] + elif self.__udc not in candidates: + raise RuntimeError(f"Can't find selected UDC: {self.__udc}") + get_logger().info("Using UDC %s", self.__udc) + + async def run(self) -> None: + logger = get_logger(0) + while True: + try: + while True: + await self._notifier.notify() + if os.path.isdir(self.__get_driver_path()): + break + await asyncio.sleep(5) + + with Inotify() as inotify: + inotify.watch(self.__get_driver_path(), InotifyMask.ALL_MODIFY_EVENTS) + await self._notifier.notify() + while True: + need_restart = False + need_notify = False + for event in (await inotify.get_series(timeout=1)): + need_notify = True + if event.mask & (InotifyMask.DELETE_SELF | InotifyMask.MOVE_SELF | InotifyMask.UNMOUNT): + logger.warning("Got fatal inotify event: %s; reinitializing OTG-bind ...", event) + need_restart = True + break + if need_restart: + break + if need_notify: + await self._notifier.notify() + except Exception: + logger.exception("Unexpected OTG-bind watcher error") + + def cleanup(self) -> None: + pass + + def read(self, pin: int) -> bool: + _ = pin + return os.path.islink(self.__get_driver_path(self.__udc)) + + def write(self, pin: int, state: bool) -> None: + _ = pin + with open(self.__get_driver_path("bind" if state else "unbind"), "w") as ctl_file: + ctl_file.write(f"{self.__udc}\n") + + def __get_driver_path(self, name: str="") -> str: + path = f"{env.SYSFS_PREFIX}/sys/bus/platform/drivers/dwc2" + return (os.path.join(path, name) if name else path) + + def __str__(self) -> str: + return f"GPIO({self._instance_name})" + + __repr__ = __str__ |