summaryrefslogtreecommitdiff
path: root/kvmd
diff options
context:
space:
mode:
authorDevaev Maxim <[email protected]>2020-12-26 04:55:40 +0300
committerDevaev Maxim <[email protected]>2020-12-26 04:55:40 +0300
commit570dd04e29f5c597cf1fdcf9eca8f9f9877d243d (patch)
tree3f0f495728ac110a80c1232aa6e79300473aeb14 /kvmd
parent77771bea55dad3c22694438e584b79d2ce6f7d48 (diff)
otgbind pseudo-gpio plugin
Diffstat (limited to 'kvmd')
-rw-r--r--kvmd/apps/__init__.py3
-rw-r--r--kvmd/apps/kvmd/__init__.py2
-rw-r--r--kvmd/apps/kvmd/ugpio.py3
-rw-r--r--kvmd/apps/otg/__init__.py7
-rw-r--r--kvmd/plugins/ugpio/otgbind.py119
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__