summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDevaev Maxim <[email protected]>2018-09-26 02:11:23 +0300
committerDevaev Maxim <[email protected]>2018-09-26 02:11:23 +0300
commit940989b6e9a3fe31bd236e4bed96e1d4778a403b (patch)
tree0ed5679b56b198db88b27390b92e29019cd4ab6f
parent6e9a3222ce566f6430caad541c2695255cb06d7c (diff)
v2 beta
-rw-r--r--kvmd/.bumpversion.cfg4
-rw-r--r--kvmd/Makefile3
-rw-r--r--kvmd/PKGBUILD40
-rw-r--r--kvmd/configs/kvmd/v1.yaml20
-rw-r--r--kvmd/configs/kvmd/v2.yaml72
-rw-r--r--kvmd/configs/nginx/nginx.conf8
-rw-r--r--kvmd/kvmd/__init__.py2
-rw-r--r--kvmd/kvmd/server.py25
-rw-r--r--kvmd/kvmd/streamer.py39
-rw-r--r--kvmd/testenv/Dockerfile2
-rw-r--r--kvmd/testenv/kvmd.yaml17
-rw-r--r--kvmd/web/css/stream.css2
-rw-r--r--kvmd/web/index.html6
-rw-r--r--kvmd/web/js/stream.js57
-rw-r--r--os/Makefile21
-rw-r--r--os/platforms/common/Dockerfile.part51
-rw-r--r--os/platforms/common/customizepkg.nginx (renamed from os/platforms/v1/customizepkg.nginx)0
-rw-r--r--os/platforms/common/motd (renamed from os/platforms/v1/motd)0
-rw-r--r--os/platforms/common/sysctl.conf (renamed from os/platforms/v1/sysctl.conf)0
-rw-r--r--os/platforms/v1/Dockerfile.part48
-rw-r--r--os/platforms/v2/Dockerfile.part11
-rw-r--r--os/platforms/v2/config.txt5
-rw-r--r--os/platforms/v2/modules.load1
-rw-r--r--os/platforms/v2/udev.rules4
24 files changed, 265 insertions, 173 deletions
diff --git a/kvmd/.bumpversion.cfg b/kvmd/.bumpversion.cfg
index e18fb505..b335e869 100644
--- a/kvmd/.bumpversion.cfg
+++ b/kvmd/.bumpversion.cfg
@@ -15,6 +15,6 @@ search = version="{current_version}"
replace = version="{new_version}"
[bumpversion:file:PKGBUILD]
-search = pkgver="{current_version}"
-replace = pkgver="{new_version}"
+search = pkgver={current_version}
+replace = pkgver={new_version}
diff --git a/kvmd/Makefile b/kvmd/Makefile
index 0f0e3e5b..aa654735 100644
--- a/kvmd/Makefile
+++ b/kvmd/Makefile
@@ -20,7 +20,8 @@ all:
run:
- docker build --rm --tag $(TESTENV_IMAGE) -f testenv/Dockerfile .
+ sudo modprobe loop
+ docker build $(TESTENV_OPTS) --rm --tag $(TESTENV_IMAGE) -f testenv/Dockerfile .
- docker run --rm \
--volume `pwd`/kvmd:/kvmd:ro \
--volume `pwd`/web:/usr/share/kvmd/web:ro \
diff --git a/kvmd/PKGBUILD b/kvmd/PKGBUILD
index a32fbf1a..853951b1 100644
--- a/kvmd/PKGBUILD
+++ b/kvmd/PKGBUILD
@@ -2,26 +2,26 @@
# Author: Maxim Devaev <[email protected]>
-pkgname="kvmd"
-pkgver="0.66"
-pkgrel="1"
+pkgname=kvmd
+pkgver=0.66
+pkgrel=1
pkgdesc="The main Pi-KVM daemon"
-arch=("any")
url="https://github.com/pi-kvm/pi-kvm"
-license=("GPL")
+license=(GPL)
+arch=(any)
depends=(
- "python"
- "python-yaml"
- "python-aiohttp"
- "python-aiofiles"
- "python-pyudev"
- "python-raspberry-gpio"
- "python-pyserial"
- "python-setproctitle"
+ python
+ python-yaml
+ python-aiohttp
+ python-aiofiles
+ python-pyudev
+ python-raspberry-gpio
+ python-pyserial
+ python-setproctitle
)
-makedepends=("python-setuptools")
+makedepends=(python-setuptools)
source=("$url/archive/v$pkgver.tar.gz")
-md5sums=("SKIP")
+md5sums=(SKIP)
build() {
@@ -34,9 +34,9 @@ build() {
package() {
cd $srcdir/$pkgname-build
- python setup.py install --root=$pkgdir
- install -Dm644 configs/kvmd.service "$pkgdir"/usr/lib/systemd/system/kvmd.service
- mkdir -p "$pkgdir"/usr/share/kvmd
- cp -r web "$pkgdir"/usr/share/kvmd
- cp -r configs "$pkgdir"/usr/share/kvmd
+ python setup.py install --root="$pkgdir"
+ install -Dm644 configs/kvmd.service "$pkgdir/usr/lib/systemd/system/kvmd.service"
+ mkdir -p "$pkgdir/usr/share/kvmd"
+ cp -r web "$pkgdir/usr/share/kvmd"
+ cp -r configs "$pkgdir/usr/share/kvmd"
}
diff --git a/kvmd/configs/kvmd/v1.yaml b/kvmd/configs/kvmd/v1.yaml
index 712891a0..bd763c81 100644
--- a/kvmd/configs/kvmd/v1.yaml
+++ b/kvmd/configs/kvmd/v1.yaml
@@ -36,15 +36,21 @@ kvmd:
init_restart_after: 1.0
shutdown_delay: 10.0
- resolutions:
- - 800x600 - 720x576
+ quality: 80
cmd:
- - "/usr/bin/mjpg_streamer"
- - "-i"
- - "input_uvc.so -d /dev/kvmd-streamer -e 2 -t pal -y -n -r {resolution}"
- - "-o"
- - "output_http.so -l localhost -p 8082"
+ - "/usr/bin/ustreamer"
+ - "--device=/dev/kvmd-streamer"
+ - "--tv-standard=pal"
+ - "--format=yuyv"
+ - "--encoder=omx"
+ - "--jpeg-quality={quality}"
+ - "--width=720"
+ - "--height=576"
+ - "--fake-width=800"
+ - "--fake-height=600"
+ - "--host=localhost"
+ - "--port=8082"
logging:
version: 1
diff --git a/kvmd/configs/kvmd/v2.yaml b/kvmd/configs/kvmd/v2.yaml
new file mode 100644
index 00000000..09c4eb33
--- /dev/null
+++ b/kvmd/configs/kvmd/v2.yaml
@@ -0,0 +1,72 @@
+kvmd:
+ server:
+ host: localhost
+ port: 8081
+ heartbeat: 3.0
+
+ hid:
+ device: /dev/ttyAMA0
+ speed: 115200
+
+ atx:
+ pinout:
+ power_led: 16
+ hdd_led: 12
+ power_switch: 26
+ reset_switch: 20
+
+ click_delay: 0.1
+ long_click_delay: 5.5
+
+ state_poll: 0.1
+
+ msd:
+ device: "/dev/kvmd-msd"
+ init_delay: 2.0
+ write_meta: true
+ chunk_size: 65536
+
+ streamer:
+ pinout:
+ cap: -1
+ conv: -1
+
+ sync_delay: 0.0
+ init_delay: 1.0
+ init_restart_after: 0.0
+ shutdown_delay: 10.0
+
+ quality: 80
+
+ cmd:
+ - "/usr/bin/ustreamer"
+ - "--device=/dev/kvmd-streamer"
+ - "--format=uyvy"
+ - "--encoder=omx"
+ - "--jpeg-quality={quality}"
+ - "--dv-timings"
+ - "--host=localhost"
+ - "--port=8082"
+
+logging:
+ version: 1
+ disable_existing_loggers: false
+
+ formatters:
+ console:
+ (): logging.Formatter
+ style: "{"
+ datefmt: "%H:%M:%S"
+ format: "[{asctime}] {name:20.20} {levelname:>7} --- {message}"
+
+ handlers:
+ console:
+ level: DEBUG
+ class: logging.StreamHandler
+ stream: ext://sys.stdout
+ formatter: console
+
+ root:
+ level: INFO
+ handlers:
+ - console
diff --git a/kvmd/configs/nginx/nginx.conf b/kvmd/configs/nginx/nginx.conf
index e0744769..f0d6965b 100644
--- a/kvmd/configs/nginx/nginx.conf
+++ b/kvmd/configs/nginx/nginx.conf
@@ -31,7 +31,7 @@ http {
server localhost:8081 fail_timeout=0s max_fails=0;
}
- upstream mjpg_streamer {
+ upstream ustreamer {
server localhost:8082 fail_timeout=0s max_fails=0;
}
@@ -112,9 +112,9 @@ http {
include /etc/nginx/proxy-params.conf;
}
- location ~ ^/streamer/(snapshot|stream)(?:/(.*))?$ {
- rewrite /streamer/?(.*)(?:/(.*))?$ /?action=$1 break;
- proxy_pass http://mjpg_streamer;
+ location /streamer {
+ rewrite /streamer/?(.*) /$1 break;
+ proxy_pass http://ustreamer;
include /etc/nginx/proxy-params.conf;
proxy_buffering off;
proxy_ignore_headers X-Accel-Buffering;
diff --git a/kvmd/kvmd/__init__.py b/kvmd/kvmd/__init__.py
index 40eead3b..110b80a7 100644
--- a/kvmd/kvmd/__init__.py
+++ b/kvmd/kvmd/__init__.py
@@ -49,7 +49,7 @@ def main() -> None:
sync_delay=float(config["streamer"]["sync_delay"]),
init_delay=float(config["streamer"]["init_delay"]),
init_restart_after=float(config["streamer"]["init_restart_after"]),
- resolutions=config["streamer"]["resolutions"],
+ quality=int(config["streamer"]["quality"]),
cmd=list(map(str, config["streamer"]["cmd"])),
loop=loop,
)
diff --git a/kvmd/kvmd/server.py b/kvmd/kvmd/server.py
index afce2c32..19282fc2 100644
--- a/kvmd/kvmd/server.py
+++ b/kvmd/kvmd/server.py
@@ -128,7 +128,7 @@ class Server: # pylint: disable=too-many-instance-attributes
self.__system_tasks: List[asyncio.Task] = []
self.__reset_streamer = False
- self.__streamer_resolution = streamer.get_current_resolution()
+ self.__streamer_quality = streamer.get_current_quality()
def run(self, host: str, port: int) -> None:
self.__hid.start()
@@ -166,7 +166,7 @@ class Server: # pylint: disable=too-many-instance-attributes
# ===== INFO
- async def __info_handler(self, _: aiohttp.web.Request) -> aiohttp.web.WebSocketResponse:
+ async def __info_handler(self, _: aiohttp.web.Request) -> aiohttp.web.Response:
return _json(_get_system_info())
# ===== WEBSOCKET
@@ -305,12 +305,15 @@ class Server: # pylint: disable=too-many-instance-attributes
@_wrap_exceptions_for_web("Can't set stream params")
async def __streamer_set_params_handler(self, request: aiohttp.web.Request) -> aiohttp.web.Response:
- resolution = request.query.get("resolution")
- if resolution:
- if resolution in self.__streamer.get_available_resolutions():
- self.__streamer_resolution = resolution
- else:
- raise BadRequest("Unknown resolution %r" % (resolution))
+ quality = request.query.get("quality")
+ if quality:
+ try:
+ quality_int = int(quality)
+ if not (1 <= quality_int <= 100):
+ raise ValueError()
+ except Exception:
+ raise BadRequest("Invalid quality %r" % (quality))
+ self.__streamer_quality = quality_int
return _json()
async def __streamer_reset_handler(self, _: aiohttp.web.Request) -> aiohttp.web.Response:
@@ -356,7 +359,7 @@ class Server: # pylint: disable=too-many-instance-attributes
cur = len(self.__sockets)
if prev == 0 and cur > 0:
if not self.__streamer.is_running():
- await self.__streamer.start(self.__streamer_resolution)
+ await self.__streamer.start(self.__streamer_quality)
await self.__broadcast_event("streamer_state", **self.__streamer.get_state())
elif prev > 0 and cur == 0:
shutdown_at = time.time() + self.__streamer_shutdown_delay
@@ -365,10 +368,10 @@ class Server: # pylint: disable=too-many-instance-attributes
await self.__streamer.stop()
await self.__broadcast_event("streamer_state", **self.__streamer.get_state())
- if self.__reset_streamer or self.__streamer_resolution != self.__streamer.get_current_resolution():
+ if self.__reset_streamer or self.__streamer_quality != self.__streamer.get_current_quality():
if self.__streamer.is_running():
await self.__streamer.stop()
- await self.__streamer.start(self.__streamer_resolution, no_init_restart=True)
+ await self.__streamer.start(self.__streamer_quality, no_init_restart=True)
await self.__broadcast_event("streamer_state", **self.__streamer.get_state())
self.__reset_streamer = False
diff --git a/kvmd/kvmd/streamer.py b/kvmd/kvmd/streamer.py
index 7e3fd513..24097547 100644
--- a/kvmd/kvmd/streamer.py
+++ b/kvmd/kvmd/streamer.py
@@ -1,8 +1,6 @@
import asyncio
import asyncio.subprocess
-from collections import OrderedDict as odict
-
from typing import List
from typing import Dict
from typing import Optional
@@ -21,10 +19,8 @@ class Streamer: # pylint: disable=too-many-instance-attributes
sync_delay: float,
init_delay: float,
init_restart_after: float,
-
- resolutions: List[str],
+ quality: int,
cmd: List[str],
-
loop: asyncio.AbstractEventLoop,
) -> None:
@@ -33,26 +29,18 @@ class Streamer: # pylint: disable=too-many-instance-attributes
self.__sync_delay = sync_delay
self.__init_delay = init_delay
self.__init_restart_after = init_restart_after
-
- self.__resolutions = odict([
- (display, (real or display))
- for (display, real) in [
- (tuple(map(str.lower, map(str.strip, resolution.split("-", maxsplit=1)))) + ("",))[:2]
- for resolution in resolutions
- ]
- ])
- self.__resolution = list(self.__resolutions)[0]
+ self.__quality = quality
self.__cmd = cmd
self.__loop = loop
self.__proc_task: Optional[asyncio.Task] = None
- async def start(self, resolution: str, no_init_restart: bool=False) -> None:
+ async def start(self, quality: int, no_init_restart: bool=False) -> None:
logger = get_logger()
logger.info("Starting streamer ...")
- assert resolution in self.__resolutions, (resolution, self.__resolutions)
- self.__resolution = resolution
+ assert 1 <= quality <= 100
+ self.__quality = quality
await self.__inner_start()
if self.__init_restart_after > 0.0 and not no_init_restart:
logger.info("Stopping streamer to restart ...")
@@ -67,22 +55,13 @@ class Streamer: # pylint: disable=too-many-instance-attributes
def is_running(self) -> bool:
return bool(self.__proc_task)
- def get_current_resolution(self) -> str:
- return self.__resolution
-
- def get_available_resolutions(self) -> List[str]:
- return list(self.__resolutions)
+ def get_current_quality(self) -> int:
+ return self.__quality
def get_state(self) -> Dict:
- (width, height) = tuple(map(int, self.__resolution.split("x")))
return {
"is_running": self.is_running(),
- "size": {
- "width": width,
- "height": height,
- },
- "resolution": self.__resolution,
- "resolutions": list(self.__resolutions),
+ "quality": self.__quality,
}
async def cleanup(self) -> None:
@@ -118,7 +97,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 = [part.format(resolution=self.__resolutions[self.__resolution]) for part in self.__cmd]
+ cmd = [part.format(quality=self.__quality) for part in self.__cmd]
proc = await asyncio.create_subprocess_exec(
*cmd,
stdout=asyncio.subprocess.PIPE,
diff --git a/kvmd/testenv/Dockerfile b/kvmd/testenv/Dockerfile
index e81a74da..db7e6538 100644
--- a/kvmd/testenv/Dockerfile
+++ b/kvmd/testenv/Dockerfile
@@ -33,7 +33,7 @@ RUN pacman -Syy \
python-pip \
nginx-mainline \
nginx-mainline-mod-lua \
- mjpg-streamer \
+ ustreamer \
socat \
&& pacman -Sc --noconfirm
diff --git a/kvmd/testenv/kvmd.yaml b/kvmd/testenv/kvmd.yaml
index 1ad9e7f5..a94dcb2b 100644
--- a/kvmd/testenv/kvmd.yaml
+++ b/kvmd/testenv/kvmd.yaml
@@ -36,17 +36,16 @@ kvmd:
init_restart_after: 1.0
shutdown_delay: 10.0
- resolutions:
- - 640x480
- - 800x600
- - 1024x768
+ quality: 80
cmd:
- - "/usr/bin/mjpg_streamer"
- - "-i"
- - "input_uvc.so -d /dev/kvmd-streamer -e 2 -y -n -r {resolution}"
- - "-o"
- - "output_http.so -l 0.0.0.0 -p 8082"
+ - "/usr/bin/ustreamer"
+ - "--device=/dev/kvmd-streamer"
+ - "--jpeg-quality={quality}"
+ - "--width=800"
+ - "--height=600"
+ - "--host=0.0.0.0"
+ - "--port=8082"
logging:
version: 1
diff --git a/kvmd/web/css/stream.css b/kvmd/web/css/stream.css
index 84aa0253..0ddcf780 100644
--- a/kvmd/web/css/stream.css
+++ b/kvmd/web/css/stream.css
@@ -35,7 +35,7 @@ div.stream-box-mouse-enabled {
cursor: url("../svg/stream-mouse-cursor.svg"), pointer;
}
-select#stream-resolution-select {
+select#stream-quality-select {
margin: 8px 0 8px 0;
}
diff --git a/kvmd/web/index.html b/kvmd/web/index.html
index 850691f8..90fbd11e 100644
--- a/kvmd/web/index.html
+++ b/kvmd/web/index.html
@@ -75,9 +75,9 @@
</div>
<hr>
<div data-dont-hide-menu class="ctl-dropdown-content-text">
- Resolution:
- <select disabled id="stream-resolution-select">
- <option>640x480</option>
+ Quality:
+ <select disabled id="stream-quality-select">
+ <option>80%</option>
</select>
</div>
<hr>
diff --git a/kvmd/web/js/stream.js b/kvmd/web/js/stream.js
index 577c6026..af3e45e9 100644
--- a/kvmd/web/js/stream.js
+++ b/kvmd/web/js/stream.js
@@ -5,8 +5,7 @@ function Stream() {
var __prev_state = false;
- var __resolution = "640x480";
- var __resolutions = ["640x480"];
+ var __quality = 80;
var __normal_size = {width: 640, height: 480};
var __size_factor = 1;
@@ -14,8 +13,13 @@ function Stream() {
var __init__ = function() {
$("stream-led").title = "Stream inactive";
+ var quality = 10;
+ for (; quality <= 100; quality += 10) {
+ $("stream-quality-select").innerHTML += "<option value=\"" + quality + "\">" + quality + "%</option>";
+ }
+
tools.setOnClick($("stream-reset-button"), __clickResetButton);
- $("stream-resolution-select").onchange = __changeResolution;
+ $("stream-quality-select").onchange = __changeQuality;
$("stream-size-slider").oninput = __resize;
$("stream-size-slider").onchange = __resize;
@@ -27,12 +31,10 @@ function Stream() {
// XXX: In current implementation we don't need this event because Stream() has own state poller
var __startPoller = function() {
- var http = tools.makeRequest("GET", "/streamer/snapshot", function() {
- if (http.readyState === 2 || http.readyState === 4) {
- var status = http.status;
- http.onreadystatechange = null;
- http.abort();
- if (status !== 200) {
+ var http = tools.makeRequest("GET", "/streamer/ping", function() {
+ if (http.readyState === 4) {
+ var response = (http.status === 200 ? JSON.parse(http.responseText) : null);
+ if (http.status !== 200 || !response.stream.online) {
tools.info("Refreshing stream ...");
__prev_state = false;
$("stream-image").className = "stream-image-inactive";
@@ -40,8 +42,9 @@ function Stream() {
$("stream-led").className = "led-off";
$("stream-led").title = "Stream inactive";
$("stream-reset-button").disabled = true;
- $("stream-resolution-select").disabled = true;
- } else if (!__prev_state) {
+ $("stream-quality-select").disabled = true;
+ } else if (http.status === 200 && !__prev_state) {
+ __normal_size = response.stream.resolution;
__refreshImage();
__prev_state = true;
$("stream-image").className = "stream-image-active";
@@ -49,6 +52,7 @@ function Stream() {
$("stream-led").className = "led-on";
$("stream-led").title = "Stream is active";
$("stream-reset-button").disabled = false;
+ $("stream-quality-select").disabled = false;
}
}
});
@@ -66,11 +70,11 @@ function Stream() {
});
};
- var __changeResolution = function() {
- var resolution = $("stream-resolution-select").value;
- if (__resolution != resolution) {
- $("stream-resolution-select").disabled = true;
- var http = tools.makeRequest("POST", "/kvmd/streamer/set_params?resolution=" + resolution, function() {
+ var __changeQuality = function() {
+ var quality = parseInt($("stream-quality-select").value);
+ if (__quality != quality) {
+ $("stream-quality-select").disabled = true;
+ var http = tools.makeRequest("POST", "/kvmd/streamer/set_params?quality=" + quality, function() {
if (http.readyState === 4) {
if (http.status !== 200) {
ui.error("Can't configure stream:<br>", http.responseText);
@@ -99,25 +103,14 @@ function Stream() {
if (http.readyState === 4 && http.status === 200) {
var result = JSON.parse(http.responseText).result;
- if (__resolutions != result.resolutions) {
- tools.info("Resolutions list changed:", result.resolutions);
- $("stream-resolution-select").innerHTML = "";
- result.resolutions.forEach(function(resolution) {
- $("stream-resolution-select").innerHTML += "<option value=\"" + resolution + "\">" + resolution + "</option>";
- });
- $("stream-resolution-select").disabled = (result.resolutions.length == 1);
- __resolutions = result.resolutions;
- }
-
- if (__resolution != result.resolution) {
- tools.info("Resolution changed:", result.resolution);
- document.querySelector("#stream-resolution-select [value=\"" + result.resolution + "\"]").selected = true;
- __resolution = result.resolution;
+ if (__quality != result.quality) {
+ tools.info("Quality changed:", result.quality);
+ document.querySelector("#stream-quality-select [value=\"" + result.quality + "\"]").selected = true;
+ __quality = result.quality;
}
- __normal_size = result.size;
__applySizeFactor();
- $("stream-image").src = "/streamer/stream/" + new Date().getTime();
+ $("stream-image").src = "/streamer/stream?t=" + new Date().getTime();
}
});
};
diff --git a/os/Makefile b/os/Makefile
index d3f8e9e6..68bbb935 100644
--- a/os/Makefile
+++ b/os/Makefile
@@ -9,7 +9,12 @@ WEBUI_ADMIN_PASSWD ?= admin
# =====
_BUILD_DIR = ./.build
-_KVMD_VERSION = $(shell bash -c 'source ../kvmd/PKGBUILD; echo $$pkgver')
+
+define fetch_version
+ curl --silent "https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD?h=$(1)" \
+ | grep "^pkgver=" \
+ | grep -Po "\d+\.\d+[^\"']*"
+endef
# =====
@@ -21,24 +26,30 @@ v1:
make _pikvm PIKVM_PLATFORM=v1 PI_BOARD=rpi-2
+v2:
+ make _pikvm PIKVM_PLATFORM=v2 PI_BOARD=rpi-2
+
+
shell:
cd $(_BUILD_DIR) && make shell
_pikvm: $(_BUILD_DIR)
- rm -rf $(_BUILD_DIR)/stages/pikvm
+ rm -rf $(_BUILD_DIR)/stages/pikvm-*
rm -rf $(_BUILD_DIR)/builder/scripts/pikvm
- cp -a platforms/$(PIKVM_PLATFORM) $(_BUILD_DIR)/stages/pikvm
+ cp -a platforms/common $(_BUILD_DIR)/stages/pikvm-common
+ cp -a platforms/$(PIKVM_PLATFORM) $(_BUILD_DIR)/stages/pikvm-$(PIKVM_PLATFORM)
cd $(_BUILD_DIR) && make binfmt && make _rpi \
BUILD_OPTS=" $(BUILD_OPTS) \
- --build-arg KVMD_VERSION=$(_KVMD_VERSION) \
+ --build-arg USTREAMER_VERSION=$(call fetch_version, ustreamer) \
+ --build-arg KVMD_VERSION=$(call fetch_version, kvmd) \
--build-arg NEW_SSH_KEYGEN=$(shell uuidgen) \
--build-arg WEBUI_ADMIN_PASSWD='$(WEBUI_ADMIN_PASSWD)' \
--build-arg NEW_HTTPS_CERT=$(shell uuidgen) \
" \
PROJECT=pikvm \
BOARD=$(PI_BOARD) \
- STAGES="__init__ os watchdog ro pikvm rootssh __cleanup__" \
+ STAGES="__init__ os watchdog ro pikvm-common pikvm-$(PIKVM_PLATFORM) rootssh __cleanup__" \
LOCALE=$(LOCALE) \
TIMEZONE=$(TIMEZONE)
diff --git a/os/platforms/common/Dockerfile.part b/os/platforms/common/Dockerfile.part
new file mode 100644
index 00000000..e4308647
--- /dev/null
+++ b/os/platforms/common/Dockerfile.part
@@ -0,0 +1,51 @@
+RUN pkg-install \
+ nginx-mainline \
+ apache-tools \
+ raspberrypi-firmware \
+ v4l-utils \
+ python \
+ python-raspberry-gpio \
+ customizepkg
+RUN systemctl enable nginx
+
+COPY stages/pikvm-common/customizepkg.nginx /etc/customizepkg.d/nginx-mainline-mod-ndk
+COPY stages/pikvm-common/customizepkg.nginx /etc/customizepkg.d/nginx-mainline-mod-lua
+RUN env MAKEPKGOPTS="--skipchecksums --skippgpcheck" pkg-install nginx-mainline-mod-lua
+
+ARG USTREAMER_VERSION
+ENV USTREAMER_VERSION $USTREAMER_VERSION
+RUN echo $USTREAMER_VERSION
+RUN pkg-install ustreamer
+
+ARG KVMD_VERSION
+ENV KVMD_VERSION $KVMD_VERSION
+RUN echo $KVMD_VERSION
+RUN pkg-install kvmd
+RUN systemctl enable kvmd
+
+COPY stages/pikvm-common/sysctl.conf /etc/sysctl.d/99-pikvm.conf
+COPY stages/pikvm-common/motd /etc/
+
+RUN sed -i -e "s/console=ttyAMA0\,115200//g" /boot/cmdline.txt \
+ && sed -i -e "s/kgdboc=ttyAMA0\,115200//g" /boot/cmdline.txt
+RUN systemctl mask [email protected]
+
+RUN rm -rf /etc/nginx/* \
+ && cp /usr/share/kvmd/configs/nginx/* /etc/nginx/ \
+ && sed -i -e "s/^#PROD//g" /etc/nginx/nginx.conf
+
+ARG WEBUI_ADMIN_PASSWD
+ENV WEBUI_ADMIN_PASSWD $WEBUI_ADMIN_PASSWD
+RUN echo "$WEBUI_ADMIN_PASSWD" | htpasswd -ci /etc/nginx/htpasswd admin
+
+ARG NEW_HTTPS_CERT
+ENV NEW_HTTPS_CERT $NEW_HTTPS_CERT
+RUN echo $NEW_HTTPS_CERT
+RUN mkdir /etc/nginx/ssl \
+ && cd /etc/nginx/ssl \
+ && openssl req -new -x509 -nodes -newkey rsa:4096 -keyout server.key -out server.crt -days 3650 \
+ -subj "/C=RU/ST=Moscow/L=Moscow/O=Pi-KVM/OU=Pi-KVM/CN=localhost" \
+ && chmod 400 server.key \
+ && chmod 444 server.crt \
+ && chmod 750 /etc/nginx/ssl \
+ && chown -R root:http /etc/nginx/ssl
diff --git a/os/platforms/v1/customizepkg.nginx b/os/platforms/common/customizepkg.nginx
index 5e7db5a4..5e7db5a4 100644
--- a/os/platforms/v1/customizepkg.nginx
+++ b/os/platforms/common/customizepkg.nginx
diff --git a/os/platforms/v1/motd b/os/platforms/common/motd
index 5b157c58..5b157c58 100644
--- a/os/platforms/v1/motd
+++ b/os/platforms/common/motd
diff --git a/os/platforms/v1/sysctl.conf b/os/platforms/common/sysctl.conf
index 71db0efc..71db0efc 100644
--- a/os/platforms/v1/sysctl.conf
+++ b/os/platforms/common/sysctl.conf
diff --git a/os/platforms/v1/Dockerfile.part b/os/platforms/v1/Dockerfile.part
index 2c6b1557..77be104f 100644
--- a/os/platforms/v1/Dockerfile.part
+++ b/os/platforms/v1/Dockerfile.part
@@ -1,48 +1,4 @@
-RUN pkg-install \
- nginx-mainline \
- apache-tools \
- mjpg-streamer \
- python \
- python-raspberry-gpio \
- customizepkg
-RUN systemctl enable nginx
+COPY stages/pikvm-v1/config.txt /boot/
+COPY stages/pikvm-v1/udev.rules /etc/udev/rules.d/pikvm.rules
-COPY stages/pikvm/customizepkg.nginx /etc/customizepkg.d/nginx-mainline-mod-ndk
-COPY stages/pikvm/customizepkg.nginx /etc/customizepkg.d/nginx-mainline-mod-lua
-RUN env MAKEPKGOPTS="--skipchecksums --skippgpcheck" pkg-install nginx-mainline-mod-lua
-
-ARG KVMD_VERSION
-ENV KVMD_VERSION $KVMD_VERSION
-RUN echo $KVMD_VERSION
-RUN pkg-install kvmd
-RUN systemctl enable kvmd
-
-COPY stages/pikvm/config.txt /boot/
-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/motd /etc/
-
-RUN sed -i -e "s/console=ttyAMA0\,115200//g" /boot/cmdline.txt \
- && sed -i -e "s/kgdboc=ttyAMA0\,115200//g" /boot/cmdline.txt
-RUN systemctl mask [email protected]
-
-RUN rm -rf /etc/nginx/* \
- && cp /usr/share/kvmd/configs/nginx/* /etc/nginx/ \
- && sed -i -e "s/^#PROD//g" /etc/nginx/nginx.conf
RUN cp /usr/share/kvmd/configs/kvmd/v1.yaml /etc/kvmd.yaml
-
-ARG WEBUI_ADMIN_PASSWD
-ENV WEBUI_ADMIN_PASSWD $WEBUI_ADMIN_PASSWD
-RUN echo "$WEBUI_ADMIN_PASSWD" | htpasswd -ci /etc/nginx/htpasswd admin
-
-ARG NEW_HTTPS_CERT
-ENV NEW_HTTPS_CERT $NEW_HTTPS_CERT
-RUN echo $NEW_HTTPS_CERT
-RUN mkdir /etc/nginx/ssl \
- && cd /etc/nginx/ssl \
- && openssl req -new -x509 -nodes -newkey rsa:4096 -keyout server.key -out server.crt -days 3650 \
- -subj "/C=RU/ST=Moscow/L=Moscow/O=Pi-KVM/OU=Pi-KVM/CN=localhost" \
- && chmod 400 server.key \
- && chmod 444 server.crt \
- && chmod 750 /etc/nginx/ssl \
- && chown -R root:http /etc/nginx/ssl
diff --git a/os/platforms/v2/Dockerfile.part b/os/platforms/v2/Dockerfile.part
new file mode 100644
index 00000000..2e689418
--- /dev/null
+++ b/os/platforms/v2/Dockerfile.part
@@ -0,0 +1,11 @@
+RUN pkg-install \
+ dkms \
+ tc358743-dkms
+
+RUN sed -i -e "s/rootwait/cma=128M rootwait/g" /boot/cmdline.txt
+
+COPY stages/pikvm-v2/config.txt /boot/
+COPY stages/pikvm-v2/udev.rules /etc/udev/rules.d/pikvm.rules
+COPY stages/pikvm-v2/modules.load /etc/modules-load.d/pikvm.conf
+
+RUN cp /usr/share/kvmd/configs/kvmd/v2.yaml /etc/kvmd.yaml
diff --git a/os/platforms/v2/config.txt b/os/platforms/v2/config.txt
new file mode 100644
index 00000000..f99b10b4
--- /dev/null
+++ b/os/platforms/v2/config.txt
@@ -0,0 +1,5 @@
+gpu_mem=16
+start_x=1
+enable_uart=1
+dtoverlay=tc358743,i2c_pins_28_29=1
+dtparam=act_led_gpio=27
diff --git a/os/platforms/v2/modules.load b/os/platforms/v2/modules.load
new file mode 100644
index 00000000..5be25408
--- /dev/null
+++ b/os/platforms/v2/modules.load
@@ -0,0 +1 @@
+tc358743
diff --git a/os/platforms/v2/udev.rules b/os/platforms/v2/udev.rules
new file mode 100644
index 00000000..b26a0ff8
--- /dev/null
+++ b/os/platforms/v2/udev.rules
@@ -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=="soc", SYMLINK+="kvmd-streamer"
+KERNEL=="sd[a-z]", SUBSYSTEM=="block", KERNELS=="1-1.4:1.0", SYMLINK+="kvmd-msd"