diff options
author | Maxim Devaev <[email protected]> | 2024-02-05 16:04:28 +0200 |
---|---|---|
committer | Maxim Devaev <[email protected]> | 2024-02-05 16:04:28 +0200 |
commit | af9c2f1f59f24ea38f989627def586b90cca7524 (patch) | |
tree | 0ae0789da1f636442e249fec31ad2eddcedb97d3 /kvmd | |
parent | d3f2b57fdce87bfcf03c2b4997802752214e7317 (diff) |
Issue pikvm/pikvm#1235: Fixed gadgets on UDC re-bind
After unbind and bind, the gadgets stop working,
unless we recreate their links in the profile.
Some kind of kernel bug.
Diffstat (limited to 'kvmd')
-rw-r--r-- | kvmd/apps/otgconf/__init__.py | 32 | ||||
-rw-r--r-- | kvmd/plugins/ugpio/otgconf.py | 36 |
2 files changed, 56 insertions, 12 deletions
diff --git a/kvmd/apps/otgconf/__init__.py b/kvmd/apps/otgconf/__init__.py index 8035aade..6be00cc9 100644 --- a/kvmd/apps/otgconf/__init__.py +++ b/kvmd/apps/otgconf/__init__.py @@ -57,31 +57,51 @@ class _GadgetControl: try: yield finally: + self.__recreate_profile() time.sleep(self.__init_delay) with open(udc_path, "w") as file: file.write(udc) + def __recreate_profile(self) -> None: + # XXX: See pikvm/pikvm#1235 + # After unbind and bind, the gadgets stop working, + # unless we recreate their links in the profile. + # Some kind of kernel bug. + for func in os.listdir(self.__get_fdest_path()): + path = self.__get_fdest_path(func) + if os.path.islink(path): + try: + os.unlink(path) + os.symlink(self.__get_fsrc_path(func), path) + except (FileNotFoundError, FileExistsError): + pass + def __read_metas(self) -> Generator[dict, None, None]: for meta_name in sorted(os.listdir(self.__meta_path)): with open(os.path.join(self.__meta_path, meta_name)) as file: yield json.loads(file.read()) + def __get_fsrc_path(self, func: str) -> str: + return usb.get_gadget_path(self.__gadget, usb.G_FUNCTIONS, func) + + def __get_fdest_path(self, func: (str | None)=None) -> str: + if func is None: + return usb.get_gadget_path(self.__gadget, usb.G_PROFILE) + return usb.get_gadget_path(self.__gadget, usb.G_PROFILE, func) + def enable_functions(self, funcs: list[str]) -> None: with self.__udc_stopped(): for func in funcs: - os.symlink( - usb.get_gadget_path(self.__gadget, usb.G_FUNCTIONS, func), - usb.get_gadget_path(self.__gadget, usb.G_PROFILE, func), - ) + os.symlink(self.__get_fsrc_path(func), self.__get_fdest_path(func)) def disable_functions(self, funcs: list[str]) -> None: with self.__udc_stopped(): for func in funcs: - os.unlink(usb.get_gadget_path(self.__gadget, usb.G_PROFILE, func)) + os.unlink(self.__get_fdest_path(func)) def list_functions(self) -> None: for meta in self.__read_metas(): - enabled = os.path.exists(usb.get_gadget_path(self.__gadget, usb.G_PROFILE, meta["func"])) + enabled = os.path.exists(self.__get_fdest_path(meta["func"])) print(f"{'+' if enabled else '-'} {meta['func']} # {meta['name']}") def make_gpio_config(self) -> None: diff --git a/kvmd/plugins/ugpio/otgconf.py b/kvmd/plugins/ugpio/otgconf.py index e1eb7cc2..37a407b5 100644 --- a/kvmd/plugins/ugpio/otgconf.py +++ b/kvmd/plugins/ugpio/otgconf.py @@ -105,29 +105,53 @@ class Plugin(BaseUserGpioDriver): async def read(self, pin: str) -> bool: if pin == "udc": return self.__is_udc_enabled() - return os.path.exists(os.path.join(self.__profile_path, pin)) + return os.path.exists(self.__get_fdest_path(pin)) async def write(self, pin: str, state: bool) -> None: async with self.__lock: + if self.read(pin) == state: + return if pin == "udc": + if state: + self.__recreate_profile() self.__set_udc_enabled(state) else: if self.__is_udc_enabled(): self.__set_udc_enabled(False) try: if state: - os.symlink( - os.path.join(self.__functions_path, pin), - os.path.join(self.__profile_path, pin), - ) + os.symlink(self.__get_fsrc_path(pin), self.__get_fdest_path(pin)) else: - os.unlink(os.path.join(self.__profile_path, pin)) + os.unlink(self.__get_fdest_path(pin)) + except (FileNotFoundError, FileExistsError): + pass finally: + self.__recreate_profile() try: await asyncio.sleep(self.__init_delay) finally: self.__set_udc_enabled(True) + def __recreate_profile(self) -> None: + # XXX: See pikvm/pikvm#1235 + # After unbind and bind, the gadgets stop working, + # unless we recreate their links in the profile. + # Some kind of kernel bug. + for func in os.listdir(self.__profile_path): + path = self.__get_fdest_path(func) + if os.path.islink(path): + try: + os.unlink(path) + os.symlink(self.__get_fsrc_path(func), path) + except (FileNotFoundError, FileNotFoundError): + pass + + def __get_fsrc_path(self, func: str) -> str: + return os.path.join(self.__functions_path, func) + + def __get_fdest_path(self, func: str) -> str: + return os.path.join(self.__profile_path, func) + def __set_udc_enabled(self, enabled: bool) -> None: with open(self.__udc_path, "w") as file: file.write(self.__udc if enabled else "\n") |