В этом посте речь пойдет о сборке и установке PiKVM на Radxa ZERO 3W с HDMI-CSI bridge C790. Должен получится крепкий среднячок, а там как получится %)

cover

Hardware: intro

KVMR0ck во всех смыслах довольно типичный DIY KVM девайс и мало чем отличается от официального гайда PiKVM V2. За исключением, пожалуй, использования Radxa ZERO 3W вместо Raspberry Pi Zero 2 W. Почему не RPI Zero 2 W? Все довольно просто:

  • хотел одноплатник помощнее, на случай если KVMR0ck не взлетит
  • хотел получше WiFi, потому-что это всегда хорошо
  • хотел eMMC на борту, дабы SD card отдать под образы дисков
  • не хотел малинку

Смотрите сами как они выглядят в сравнении:

Radxa Zero 3WRaspberry Pi Zero 2 W
CPURockchip RK3566 64-bit SoC with quad‑core Arm Cortex‑A55 @ 1.6GHzBroadcom BCM2710A1 64-bit SoC with quad-core Arm Cortex-A53 @ 1GHz
RAM1GB, 2GB, 4GB, or 8GB LPDDR4512MB LPDDR2
GPUArm Mali‑G52‑2EEBroadcom VideoCore IV
PortsMicro HDMI (1080p@60fps), USB-C 2.0 OTG, USB-C 3.0 Host, MIPI CSI camera connectorMini HDMI (1080p@30fps), micro USB 2.0 OTG, micro USB 2.0 power, MIPI CSI-2 camera connector
NetworkingWi-Fi 6 and Bluetooth 5.4 with BLEWi-Fi 4 and Bluetooth 4.2 with BLE
StorageMicroSD card slot, optional on-board eMMC (8GB, 16GB, 32GB, or 64GB)MicroSD card slot
Price$20-$100$20

Конкретно у меня Radxa Zero 3W с 4GB RAM и 32GB eMMC. По деньгам получилось около $70:

Hardware: assemble

Тут сложно сделать что-то не так, ведь единственное, что предстоит изготовить - USB/Power Splitter. Можно было бы и найти готовый (e.g. USB/Power Splitter for Raspberry Pi BliKVM and PiKVM), но не вижу смысла, ведь это просто пара коннекторов с резисторами: cover Не уверен в острой необходимости pull-up/pull-down резисторов, но по стандарту они нужны - решил оставить.

Осталось дело за малым - запаять и подсобрать: cover

Software

С софтом тоже без причуд:

Cтавим Debian 12 (Bookworm) Minimal на eMMC, делаем первичную настройку (WiFi, SSH, вот это все), топаем по SSH и…

Включаем USB OTG, добавив нужный Device Tree overlay:

root@kvmr0ck:~# mkdir -p /boot/overlay-user
root@kvmr0ck:~# cat << '_EOF_' > /boot/overlay-user/usb-otg.dts
/dts-v1/;
/plugin/;

/ {
		metadata {
				title = "Enable USB OTG";
				compatible = "rockchip,rk3566", "rockchip,rk3568";
				category = "misc";
				exclusive = "usbdrd_dwc3-dr_mode";
				description = "Enable USB OTG port";
		};
};

&usbdrd_dwc3 {
		status = "okay";
		dr_mode = "otg";
};
_EOF_
root@kvmr0ck:~# armbian-add-overlay /boot/overlay-user/usb-otg.dts
Compiling the overlay
Copying the compiled overlay file to /boot/overlay-user/
Reboot is required to apply the changes
root@kvmr0ck:~# reboot

Затем поддержу HDMI-CSI. Мой C790, как и большинство подобных, выполнен на базе Toshiba TC358743, а для его поддержки какой-то добрый человек уже поработал (см. TC358743 Radxa Zero 3W Notes):

root@kvmr0ck:~# ls -la /dev/video*
ls: cannot access '/dev/video*': No such file or directory
root@kvmr0ck:~# cat << '_EOF_' > /boot/overlay-user/tc358743-hdmi-in.dts
/dts-v1/;
/plugin/;

/ {

	metadata {
		title = "TC358743 HDMI RX";
		compatible = "radxa,zero3";
		description = "Allow use of TC358743 HDMI -> CSI2 input.";
		category = "video";
	};

	fragment@0 {
		target-path = "/";

		__overlay__ {
			clk_cam_27m: external-camera-clock-27m {
				status = "okay";
				compatible = "fixed-clock";
				clock-frequency = <27000000>;
				clock-output-names = "clk_cam_27mhz";
				#clock-cells = <0>;
			};

			camera_pwdn_gpio: camera-pwdn-gpio {
				status = "okay";
				compatible = "regulator-fixed";
				regulator-name = "camera_pwdn_gpio";
				regulator-always-on;
				regulator-boot-on;
				enable-active-high;
				// gpio = <&gpio3 RK_PC6 GPIO_ACTIVE_HIGH>  // Commented so no included needed.
				gpio = <&gpio3 22 0>;
			};
		};
	};

	fragment@1 {
		target = <&i2c2>;

		__overlay__ {
			status = "okay";
			pinctrl-names = "default";
			pinctrl-0 = <&i2c2m1_xfer>;
			#address-cells = <1>;
			#size-cells = <0>;

			camera_tc358743: tc358743@f {
				status = "okay";
				compatible = "toshiba,tc358743";
				reg = <0x0f>;
				clocks = <&clk_cam_27m>;
				clock-names = "refclk";
				rockchip,camera-module-index = <0>;
				rockchip,camera-module-facing = "back";
				rockchip,camera-module-name = "tc358743";
				rockchip,camera-module-lens-name = "default";

				port {
					hdmi_rx_out: endpoint {
						remote-endpoint = <&mipi_in_hdmirx0>;
						clock-lanes = <0>;
						clock-noncontinuous;
						link-frequencies = /bits/ 64 <297000000>;
						data-lanes = <1 2 3 4>;
					};
				};
			};
		};
	};

	fragment@2 {
		target = <&csi2_dphy_hw>;

		__overlay__ {
			status = "okay";
		};
	};

	fragment@3 {
		target = <&csi2_dphy0>;

		__overlay__ {
			status = "okay";

			ports {
				#address-cells = <1>;
				#size-cells = <0>;

				port@0 {
					reg = <0>;
					#address-cells = <1>;
					#size-cells = <0>;

					mipi_in_hdmirx0: endpoint@1 {
						reg = <1>;
						remote-endpoint = <&hdmi_rx_out>;
						data-lanes = <1 2 3 4>;
					};
				};

				port@1 {
					reg = <1>;
					#address-cells = <1>;
					#size-cells = <0>;

					mipi_out_dphy0: endpoint@1 {
						reg = <1>;
						remote-endpoint = <&rkisp_mipi_in>;
						data-lanes = <1 2 3 4>;
					};
				};
			};
		};
	};
	
	fragment@4 {
		target = <&rkisp_vir0>;

		__overlay__ {
			status = "okay";

			port {
				#address-cells = <1>;
				#size-cells = <0>;

				rkisp_mipi_in: endpoint@0 {
					reg = <0>;
					remote-endpoint = <&mipi_out_dphy0>;
				};
			};
		};
	};

	fragment@5 {
		target = <&rkisp>;

		__overlay__ {
			status = "okay";
		};
	};

	fragment@6 {
		target = <&rkisp_mmu>;

		__overlay__ {
			status = "okay";
		};
	};

	fragment@7 {
		target = <&rkcif_mmu>;

		__overlay__ {
			status = "okay";
		};
	};

	fragment@8 {
		target = <&rkcif>;

		__overlay__ {
			status = "okay";
		};
	};
};
_EOF_
root@kvmr0ck:~# armbian-add-overlay /boot/overlay-user/tc358743-hdmi-in.dts
Compiling the overlay
Copying the compiled overlay file to /boot/overlay-user/
Reboot is required to apply the changes
root@kvmr0ck:~# reboot
buglloc@kvmr0ck:~$ ls -al /dev/video*
crw-rw---- 1 root video 81, 0 Dec 14 19:12 /dev/video0
crw-rw---- 1 root video 81, 1 Dec 14 19:12 /dev/video1
crw-rw---- 1 root video 81, 2 Dec 14 19:12 /dev/video2
crw-rw---- 1 root video 81, 3 Dec 14 19:12 /dev/video3
crw-rw---- 1 root video 81, 4 Dec 14 19:12 /dev/video4
crw-rw---- 1 root video 81, 5 Dec 14 19:12 /dev/video5
crw-rw---- 1 root video 81, 6 Dec 14 19:12 /dev/video6
crw-rw---- 1 root video 81, 7 Dec 14 19:12 /dev/video7
crw-rw---- 1 root video 81, 8 Dec 14 19:12 /dev/video8
crw-rw---- 1 root video 81, 9 Dec 14 19:12 /dev/video9

Осталось установить KVMD For Armbian:

root@kvmr0ck:~# apt install -y git vim make python3-dev gcc xz-utils wget sudo
root@kvmr0ck:~# git clone --depth=1 https://github.com/srepac/kvmd-armbian.git
root@kvmr0ck:~# cd kvmd-armbian
root@kvmr0ck:~/kvmd-armbian# ./install.sh
Python 3.11 is supported.
Running part 1 of PiKVM installer script v3.4 by @srepac
Single Board Computer:  Radxa ZERO 3
[...]
Press ENTER to continue or CTRL+C to break out of script.

Broadcast message from root@kvmr0ck on pts/1 (Sat 2024-12-14 19:37:20 MSK):

The system will reboot now!

root@kvmr0ck:~/kvmd-armbian# ./install.sh 
Python 3.11 is supported.

Running part 2 of PiKVM installer script v3.4 by @srepac
[...]
Check kvmd devices

lrwxrwxrwx 1 root root 5 Dec 14 19:41 /dev/kvmd-hid-keyboard -> hidg0
lrwxrwxrwx 1 root root 5 Dec 14 19:41 /dev/kvmd-hid-mouse -> hidg1
lrwxrwxrwx 1 root root 5 Dec 14 19:41 /dev/kvmd-hid-mouse-alt -> hidg2
lrwxrwxrwx 1 root root 6 Dec 14 19:41 /dev/kvmd-video -> video0

You should see devices for keyboard, mouse, and video.

Point a browser to https://kvmr0ck
Please make sure kvmd services are running after reboot.
root@kvmr0ck:~/kvmd-armbian# reboot

Проверяем, ииии:

root@kvmr0ck:~# systemctl status kvmd
● kvmd.service - PiKVM - The main daemon
	 Loaded: loaded (/lib/systemd/system/kvmd.service; enabled; preset: enabled)
	 Active: active (running) since Sat 2024-12-14 19:42:14 MSK; 1min 14s ago
   Main PID: 1081 (kvmd/main: /usr)
	  Tasks: 15 (limit: 82)
	 Memory: 78.4M
		CPU: 15.639s
	 CGroup: /system.slice/kvmd.service
			 ├─1081 "kvmd/main: /usr/bin/python /usr/bin/kvmd --run"
			 ├─1094 "kvmd/hid-keyboard: /usr/bin/python /usr/bin/kvmd --run"
			 ├─1095 "kvmd/hid-mouse: /usr/bin/python /usr/bin/kvmd --run"
			 ├─1096 "kvmd/hid-mouse: /usr/bin/python /usr/bin/kvmd --run"
			 └─1123 "kvmd/streamer: /usr/bin/ustreamer --device=/dev/kvmd-video --persistent --format=mjpeg --resolution=1280x720 --desired-fps=40 --drop-same-frames=30 --unix=/run/kvmd/ustreamer.sock --unix-rm --unix-mode=0660 >

Dec 14 19:43:24 kvmr0ck kvmd[1081]: kvmd.apps.kvmd.streamer           INFO --- => -- ERROR [85.192          ] -- CAP: Can't set device format: Invalid argument
Dec 14 19:43:25 kvmr0ck kvmd[1081]: kvmd.apps.kvmd.streamer           INFO --- => -- INFO  [86.228          ] -- Using V4L2 device: /dev/kvmd-video

Как обычно что-то пошло не так :) Видимо формат не подходящий, cмотрим что нам говорит v4l2:

root@kvmr0ck:~# v4l2-ctl --list-formats
ioctl: VIDIOC_ENUM_FMT
	Type: Video Capture Multiplanar

	[0]: 'UYVY' (UYVY 4:2:2)
	[1]: '422P' (Planar YUV 4:2:2)
	[2]: 'NV16' (Y/UV 4:2:2)
	[3]: 'NV61' (Y/VU 4:2:2)
	[4]: 'YM16' (Planar YUV 4:2:2 (N-C))
	[5]: 'NV21' (Y/VU 4:2:0)
	[6]: 'NV12' (Y/UV 4:2:0)
	[7]: 'NM21' (Y/VU 4:2:0 (N-C))
	[8]: 'NM12' (Y/UV 4:2:0 (N-C))
	[9]: 'YU12' (Planar YUV 4:2:0)
	[10]: 'YM24' (Planar YUV 4:4:4 (N-C))

Переопределяем настройки стримера:

root@kvmr0ck:~# cat << '_EOF_' > /etc/kvmd/override.yaml 
kvmd:
	hid:
		mouse_alt:
			device: /dev/kvmd-hid-mouse-alt  # allow relative mouse mode
	msd:
		type: disabled
	atx:
		type: disabled
	streamer:
		quality: 0
		resolution:
			default: 1280x720
			available:
				- 1920x1080
				- 1600x1200
				- 1360x768
				- 1280x1024
				- 1280x960
				- 1280x800
				- 1280x720
				- 1024x768
				- 800x600
				- 720x576
				- 720x480
				- 640x480
		cmd:
			- "/usr/bin/ustreamer"
			- "--device=/dev/kvmd-video"
			- "--persistent"
			- "--format=uyvy"
			- "--resolution={resolution}"
			- "--desired-fps={desired_fps}"
			- "--drop-same-frames=30"
			- "--unix={unix}"
			- "--unix-rm"
			- "--unix-mode=0660"
			- "--exit-on-parent-death"
			- "--process-name-prefix={process_name_prefix}"
			- "--notify-parent"
			- "--no-log-colors"
			- "--jpeg-sink=kvmd::ustreamer::jpeg"
			- "--jpeg-sink-mode=0660"
			- "--slowdown"
_EOF_

И снова проверяем:

root@kvmr0ck:~# systemctl restart kvmd
root@kvmr0ck:~# systemctl status kvmd
● kvmd.service - PiKVM - The main daemon
	 Loaded: loaded (/lib/systemd/system/kvmd.service; enabled; preset: enabled)
	 Active: active (running) since Sat 2024-12-14 19:51:03 MSK; 48s ago
   Main PID: 5151 (kvmd/main: /usr)
	  Tasks: 29 (limit: 82)
	 Memory: 66.7M
		CPU: 10.147s
	 CGroup: /system.slice/kvmd.service
			 ├─5151 "kvmd/main: /usr/bin/python /usr/bin/kvmd --run"
			 ├─5156 "kvmd/hid-keyboard: /usr/bin/python /usr/bin/kvmd --run"
			 ├─5157 "kvmd/hid-mouse: /usr/bin/python /usr/bin/kvmd --run"
			 ├─5158 "kvmd/hid-mouse: /usr/bin/python /usr/bin/kvmd --run"
			 └─5511 "kvmd/streamer: /usr/bin/ustreamer --device=/dev/kvmd-video --persistent --format=uyvy --resolution=1280x720 --desired-fps=40 --drop-same-frames=30 --unix=/run/kvmd/ustreamer.sock --unix-rm --unix-mode=0660 ->

Dec 14 19:51:50 kvmr0ck kvmd[5151]: kvmd.apps.kvmd.streamer           INFO --- => -- INFO  [591.649          ] -- CAP: Using capture type: multi-planar
Dec 14 19:51:50 kvmr0ck kvmd[5151]: kvmd.apps.kvmd.streamer           INFO --- => -- INFO  [591.649          ] -- CAP: Using resolution: 1280x720
Dec 14 19:51:50 kvmr0ck kvmd[5151]: kvmd.apps.kvmd.streamer           INFO --- => -- INFO  [591.649          ] -- CAP: Using format: UYVY
Dec 14 19:51:50 kvmr0ck kvmd[5151]: kvmd.apps.kvmd.streamer           INFO --- => -- INFO  [591.649          ] -- CAP: Querying HW FPS changing is not supported
Dec 14 19:51:50 kvmr0ck kvmd[5151]: kvmd.apps.kvmd.streamer           INFO --- => -- INFO  [591.649          ] -- CAP: Using IO method: MMAP
Dec 14 19:51:50 kvmr0ck kvmd[5151]: kvmd.apps.kvmd.streamer           INFO --- => -- INFO  [591.654          ] -- CAP: Requested 5 device buffers, got 5
Dec 14 19:51:50 kvmr0ck kvmd[5151]: kvmd.apps.kvmd.streamer           INFO --- => -- INFO  [591.663          ] -- CAP: Capturing started
Dec 14 19:51:50 kvmr0ck kvmd[5151]: kvmd.apps.kvmd.streamer           INFO --- => -- INFO  [591.663          ] -- Using JPEG quality: 80%
Dec 14 19:51:50 kvmr0ck kvmd[5151]: kvmd.apps.kvmd.streamer           INFO --- => -- INFO  [591.663          ] -- Creating pool JPEG with 4 workers ...
Dec 14 19:51:50 kvmr0ck kvmd[5151]: kvmd.apps.kvmd.streamer           INFO --- => -- INFO  [591.665          ] -- Capturing ...

Выглядит как успех!

Demo

Коль все готово - почему бы не посмотреть в деле:

Closure

Стоит ли получившийся KVM своих денег, сложно сказать. Скорее нет, чем да. Но! Но он работает! Работает достаточно хорошо, чтобы делать траблшутинг любой сложности и в целом иметь в кармане довольно мощную железку. Да, есть заметный input lag и в игрушки не поиграешь, но так ли это важно для типичного ops и при цене одного вечера в пабе?

Вот что действительно расстроило, так это CSI разъем у Radxa Zero 3W. Какой-то он хлипкий и нуждается в улучшении, может доберусь до колхозинга, и вы прочтете еще один пост. Кто знает ;)

Небольшое сравнение с остальными представителями читайте в KVMBattle.