udev instead own bycicles

This commit is contained in:
Devaev Maxim 2018-07-07 23:37:38 +00:00
parent f9a69c7467
commit 476018aeb8
10 changed files with 30 additions and 151 deletions

View File

@ -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"]),

View File

@ -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")

View File

@ -1,2 +0,0 @@
from . import main
main()

View File

@ -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:

View File

@ -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,

View File

@ -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",
],
},

View File

@ -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/

View File

@ -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"

View 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"