mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2025-12-12 01:00:29 +08:00
udev instead own bycicles
This commit is contained in:
parent
f9a69c7467
commit
476018aeb8
@ -34,16 +34,15 @@ def main() -> None:
|
||||
)
|
||||
|
||||
msd = MassStorageDevice(
|
||||
bind=str(config["msd"]["bind"]),
|
||||
device_path=str(config["msd"]["device"]),
|
||||
init_delay=float(config["msd"]["init_delay"]),
|
||||
write_meta=bool(config["msd"]["write_meta"]),
|
||||
loop=loop,
|
||||
)
|
||||
|
||||
streamer = Streamer(
|
||||
cap_power=int(config["streamer"]["pinout"].get("cap", -1)),
|
||||
conv_power=int(config["streamer"]["pinout"].get("conv", -1)),
|
||||
bind=str(config["streamer"].get("bind", "")),
|
||||
cap_power=int(config["streamer"]["pinout"]["cap"]),
|
||||
conv_power=int(config["streamer"]["pinout"]["conv"]),
|
||||
sync_delay=float(config["streamer"]["sync_delay"]),
|
||||
init_delay=float(config["streamer"]["init_delay"]),
|
||||
width=int(config["streamer"]["size"]["width"]),
|
||||
|
||||
@ -1,48 +0,0 @@
|
||||
import argparse
|
||||
|
||||
from ... import msd
|
||||
from ... import streamer
|
||||
|
||||
|
||||
# =====
|
||||
def _probe_msd(path: str) -> bool:
|
||||
info = msd.explore_device(path)
|
||||
if info:
|
||||
print("It's a mass-storage device")
|
||||
print("--------------------------")
|
||||
print("Path: ", info.path)
|
||||
print("Bind: ", info.bind)
|
||||
print("Size: ", info.size)
|
||||
print("Manufacturer:", info.manufacturer)
|
||||
print("Product: ", info.product)
|
||||
print("Serial: ", info.serial)
|
||||
print("Image name: ", info.image_name)
|
||||
assert msd.locate_by_bind(info.bind), info.bind
|
||||
return bool(info)
|
||||
|
||||
|
||||
def _probe_streamer(path: str) -> bool:
|
||||
info = streamer.explore_device(path)
|
||||
if info:
|
||||
print("It's a streamer device")
|
||||
print("----------------------")
|
||||
print("Path: ", info.path)
|
||||
print("Bind: ", info.bind)
|
||||
print("Driver:", info.driver)
|
||||
assert streamer.locate_by_bind(info.bind), info.bind
|
||||
return bool(info)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("device")
|
||||
options = parser.parse_args()
|
||||
|
||||
for probe in [
|
||||
_probe_msd,
|
||||
_probe_streamer,
|
||||
]:
|
||||
if probe(options.device):
|
||||
break
|
||||
else:
|
||||
raise RuntimeError("Can't recognize device")
|
||||
@ -1,2 +0,0 @@
|
||||
from . import main
|
||||
main()
|
||||
@ -25,7 +25,7 @@ class MassStorageError(Exception):
|
||||
|
||||
class IsNotOperationalError(MassStorageError):
|
||||
def __init__(self) -> None:
|
||||
super().__init__("Missing bind for mass-storage device")
|
||||
super().__init__("Missing path for mass-storage device")
|
||||
|
||||
|
||||
class AlreadyConnectedToPcError(MassStorageError):
|
||||
@ -50,7 +50,6 @@ class IsBusyError(MassStorageError):
|
||||
|
||||
class MassStorageDeviceInfo(NamedTuple):
|
||||
path: str
|
||||
bind: str
|
||||
size: int
|
||||
manufacturer: str
|
||||
product: str
|
||||
@ -108,10 +107,6 @@ def explore_device(path: str) -> Optional[MassStorageDeviceInfo]:
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
interface_device = block_device.find_parent("usb", "usb_interface")
|
||||
if not interface_device:
|
||||
return None
|
||||
|
||||
usb_device = block_device.find_parent("usb", "usb_device")
|
||||
if not usb_device:
|
||||
return None
|
||||
@ -122,7 +117,6 @@ def explore_device(path: str) -> Optional[MassStorageDeviceInfo]:
|
||||
|
||||
return MassStorageDeviceInfo(
|
||||
path=path,
|
||||
bind=interface_device.sys_name,
|
||||
size=size,
|
||||
manufacturer=usb_device.attributes.asstring("manufacturer").strip(),
|
||||
product=usb_device.attributes.asstring("product").strip(),
|
||||
@ -131,24 +125,11 @@ def explore_device(path: str) -> Optional[MassStorageDeviceInfo]:
|
||||
)
|
||||
|
||||
|
||||
def locate_by_bind(bind: str) -> str:
|
||||
ctx = pyudev.Context()
|
||||
for device in ctx.list_devices(subsystem="block"):
|
||||
storage_device = device.find_parent("usb", "usb_interface")
|
||||
if storage_device:
|
||||
try:
|
||||
device.attributes.asint("partititon")
|
||||
except KeyError:
|
||||
if storage_device.sys_name == bind:
|
||||
return os.path.join("/dev", device.sys_name)
|
||||
return ""
|
||||
|
||||
|
||||
def _operated_and_locked(method: Callable) -> Callable:
|
||||
async def wrap(self: "MassStorageDevice", *args: Any, **kwargs: Any) -> Any:
|
||||
if self._device_file: # pylint: disable=protected-access
|
||||
raise IsBusyError()
|
||||
if not self._bind: # pylint: disable=protected-access
|
||||
if not self._device_path: # pylint: disable=protected-access
|
||||
IsNotOperationalError()
|
||||
async with self._lock: # pylint: disable=protected-access
|
||||
return (await method(self, *args, **kwargs))
|
||||
@ -156,8 +137,15 @@ def _operated_and_locked(method: Callable) -> Callable:
|
||||
|
||||
|
||||
class MassStorageDevice: # pylint: disable=too-many-instance-attributes
|
||||
def __init__(self, bind: str, init_delay: float, write_meta: bool, loop: asyncio.AbstractEventLoop) -> None:
|
||||
self._bind = bind
|
||||
def __init__(
|
||||
self,
|
||||
device_path: str,
|
||||
init_delay: float,
|
||||
write_meta: bool,
|
||||
loop: asyncio.AbstractEventLoop,
|
||||
) -> None:
|
||||
|
||||
self._device_path = device_path
|
||||
self.__init_delay = init_delay
|
||||
self.__write_meta = write_meta
|
||||
self.__loop = loop
|
||||
@ -168,8 +156,8 @@ class MassStorageDevice: # pylint: disable=too-many-instance-attributes
|
||||
self.__writed = 0
|
||||
|
||||
logger = get_logger(0)
|
||||
if self._bind:
|
||||
logger.info("Using bind %r as mass-storage device", self._bind)
|
||||
if self._device_path:
|
||||
logger.info("Using %r as mass-storage device", self._device_path)
|
||||
try:
|
||||
logger.info("Enabled metadata writing")
|
||||
loop.run_until_complete(self.connect_to_kvm(no_delay=True))
|
||||
@ -179,9 +167,9 @@ class MassStorageDevice: # pylint: disable=too-many-instance-attributes
|
||||
else:
|
||||
log = logger.exception
|
||||
log("Mass-storage device is not operational: %s", err)
|
||||
self._bind = ""
|
||||
self._device_path = ""
|
||||
else:
|
||||
logger.warning("Missing bind; mass-storage device is not operational")
|
||||
logger.warning("Mass-storage device is not operational")
|
||||
|
||||
@_operated_and_locked
|
||||
async def connect_to_kvm(self, no_delay: bool=False) -> None:
|
||||
@ -203,7 +191,7 @@ class MassStorageDevice: # pylint: disable=too-many-instance-attributes
|
||||
|
||||
def get_state(self) -> Dict:
|
||||
return {
|
||||
"in_operate": bool(self._bind),
|
||||
"in_operate": bool(self._device_path),
|
||||
"connected_to": ("kvm" if self.__device_info else "server"),
|
||||
"is_busy": bool(self._device_file),
|
||||
"writed": self.__writed,
|
||||
@ -255,12 +243,9 @@ class MassStorageDevice: # pylint: disable=too-many-instance-attributes
|
||||
await self.__close_device_file()
|
||||
|
||||
async def __reread_device_info(self) -> None:
|
||||
device_path = await self.__loop.run_in_executor(None, locate_by_bind, self._bind)
|
||||
if not device_path:
|
||||
raise MassStorageError("Can't locate device by bind %r" % (self._bind))
|
||||
device_info = await self.__loop.run_in_executor(None, explore_device, device_path)
|
||||
device_info = await self.__loop.run_in_executor(None, explore_device, self._device_path)
|
||||
if not device_info:
|
||||
raise MassStorageError("Can't explore device %r" % (device_path))
|
||||
raise MassStorageError("Can't explore device %r" % (self._device_path))
|
||||
self.__device_info = device_info
|
||||
|
||||
async def __close_device_file(self) -> None:
|
||||
|
||||
@ -1,14 +1,9 @@
|
||||
import os
|
||||
import asyncio
|
||||
import asyncio.subprocess
|
||||
|
||||
from typing import List
|
||||
from typing import Dict
|
||||
from typing import NamedTuple
|
||||
from typing import Optional
|
||||
from typing import Any
|
||||
|
||||
import pyudev
|
||||
|
||||
from .logging import get_logger
|
||||
|
||||
@ -16,52 +11,11 @@ from . import gpio
|
||||
|
||||
|
||||
# =====
|
||||
class StreamerDeviceInfo(NamedTuple):
|
||||
path: str
|
||||
bind: str
|
||||
driver: str
|
||||
|
||||
|
||||
def explore_device(path: str) -> Optional[StreamerDeviceInfo]:
|
||||
# udevadm info -a -p $(udevadm info -q path -n /dev/sda)
|
||||
ctx = pyudev.Context()
|
||||
|
||||
video_device = pyudev.Devices.from_device_file(ctx, path)
|
||||
if video_device.subsystem != "video4linux":
|
||||
return None
|
||||
try:
|
||||
if int(video_device.attributes.get("index")) != 0:
|
||||
# My strange laptop configuration
|
||||
return None
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
interface_device = video_device.find_parent("usb", "usb_interface")
|
||||
if not interface_device:
|
||||
return None
|
||||
|
||||
return StreamerDeviceInfo(
|
||||
path=path,
|
||||
bind=interface_device.sys_name,
|
||||
driver=interface_device.driver,
|
||||
)
|
||||
|
||||
|
||||
def locate_by_bind(bind: str) -> str:
|
||||
ctx = pyudev.Context()
|
||||
for device in ctx.list_devices(subsystem="video4linux"):
|
||||
interface_device = device.find_parent("usb", "usb_interface")
|
||||
if interface_device and interface_device.sys_name == bind:
|
||||
return os.path.join("/dev", device.sys_name)
|
||||
return ""
|
||||
|
||||
|
||||
class Streamer: # pylint: disable=too-many-instance-attributes
|
||||
def __init__(
|
||||
self,
|
||||
cap_power: int,
|
||||
conv_power: int,
|
||||
bind: str,
|
||||
sync_delay: float,
|
||||
init_delay: float,
|
||||
width: int,
|
||||
@ -70,11 +24,8 @@ class Streamer: # pylint: disable=too-many-instance-attributes
|
||||
loop: asyncio.AbstractEventLoop,
|
||||
) -> None:
|
||||
|
||||
assert cmd, cmd
|
||||
|
||||
self.__cap_power = (gpio.set_output(cap_power) if cap_power > 0 else cap_power)
|
||||
self.__conv_power = (gpio.set_output(conv_power) if conv_power > 0 else conv_power)
|
||||
self.__bind = bind
|
||||
self.__sync_delay = sync_delay
|
||||
self.__init_delay = init_delay
|
||||
self.__width = width
|
||||
@ -132,15 +83,7 @@ class Streamer: # pylint: disable=too-many-instance-attributes
|
||||
while True: # pylint: disable=too-many-nested-blocks
|
||||
proc: Optional[asyncio.subprocess.Process] = None # pylint: disable=no-member
|
||||
try:
|
||||
cmd_placeholders: Dict[str, Any] = {"width": self.__width, "height": self.__height}
|
||||
if self.__bind:
|
||||
logger.info("Using bind %r as streamer device", self.__bind)
|
||||
device_path = await self.__loop.run_in_executor(None, locate_by_bind, self.__bind)
|
||||
if not device_path:
|
||||
raise RuntimeError("Can't locate device by bind %r" % (self.__bind))
|
||||
cmd_placeholders["device"] = device_path
|
||||
cmd = [part.format(**cmd_placeholders) for part in self.__cmd]
|
||||
|
||||
cmd = [part.format(width=self.__width, height=self.__height) for part in self.__cmd]
|
||||
proc = await asyncio.create_subprocess_exec(
|
||||
*cmd,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
|
||||
@ -24,7 +24,6 @@ def main() -> None:
|
||||
"kvmd.extras",
|
||||
"kvmd.extras.cleanup",
|
||||
"kvmd.extras.wscli",
|
||||
"kvmd.extras.explorehw",
|
||||
],
|
||||
|
||||
entry_points={
|
||||
@ -32,7 +31,6 @@ def main() -> None:
|
||||
"kvmd = kvmd:main",
|
||||
"kvmd-cleanup = kvmd.extras.cleanup:main",
|
||||
"kvmd-wscli = kvmd.extras.wscli:main",
|
||||
"kvmd-explorehw = kvmd.extras.explorehw:main",
|
||||
],
|
||||
},
|
||||
|
||||
|
||||
@ -4,7 +4,8 @@ RUN pkg-install \
|
||||
nginx
|
||||
|
||||
COPY stages/pikvm/config.txt /boot/
|
||||
COPY stages/pikvm/99-pikvm.conf /etc/sysctl.d/
|
||||
COPY stages/pikvm/sysctl.conf /etc/sysctl.d/99-pikvm.conf
|
||||
COPY stages/pikvm/udev.rules /etc/udev/rules.d/pikvm.rules
|
||||
COPY stages/pikvm/index.html /srv/http/
|
||||
COPY stages/pikvm/kvmd.yaml /etc/
|
||||
COPY stages/pikvm/nginx.conf /etc/nginx/
|
||||
|
||||
@ -24,8 +24,7 @@ kvmd:
|
||||
state_poll: 0.1
|
||||
|
||||
msd:
|
||||
# FIXME: It's for laptop lol
|
||||
bind: "1-2:1.0"
|
||||
device: "/dev/kvmd-msd"
|
||||
init_delay: 2.0
|
||||
write_meta: true
|
||||
chunk_size: 8192
|
||||
@ -46,7 +45,7 @@ kvmd:
|
||||
cmd:
|
||||
- "/usr/bin/mjpg_streamer"
|
||||
- "-i"
|
||||
- "input_uvc.so -d /dev/video0 -e 2 -y -n -r {width}x{height}"
|
||||
- "input_uvc.so -d /dev/kvmd-streamer -e 2 -y -n -r {width}x{height}"
|
||||
- "-o"
|
||||
- "output_http.so -l localhost -p 8082"
|
||||
|
||||
|
||||
4
os/platforms/v1/udev.rules
Normal file
4
os/platforms/v1/udev.rules
Normal file
@ -0,0 +1,4 @@
|
||||
# https://unix.stackexchange.com/questions/66901/how-to-bind-usb-device-under-a-static-name
|
||||
# https://wiki.archlinux.org/index.php/Udev#Setting_static_device_names
|
||||
KERNEL=="video[0-9]*", SUBSYSTEM=="video4linux", KERNELS=="1-1.3:1.0", SYMLINK+="kvmd-streamer"
|
||||
KERNEL=="sd[a-z]", SUBSYSTEM=="block", KERNELS=="1-1.4:1.0", SYMLINK+="kvmd-msd"
|
||||
Loading…
x
Reference in New Issue
Block a user