fix(sync): recover mjpeg uvc install path
This commit is contained in:
parent
fb9c7b9813
commit
ee7550dfe5
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -1642,7 +1642,7 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lesavka_client"
|
name = "lesavka_client"
|
||||||
version = "0.14.41"
|
version = "0.14.42"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-stream",
|
"async-stream",
|
||||||
@ -1676,7 +1676,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lesavka_common"
|
name = "lesavka_common"
|
||||||
version = "0.14.41"
|
version = "0.14.42"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64",
|
"base64",
|
||||||
@ -1688,7 +1688,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lesavka_server"
|
name = "lesavka_server"
|
||||||
version = "0.14.41"
|
version = "0.14.42"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64",
|
"base64",
|
||||||
|
|||||||
@ -4,7 +4,7 @@ path = "src/main.rs"
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "lesavka_client"
|
name = "lesavka_client"
|
||||||
version = "0.14.41"
|
version = "0.14.42"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lesavka_common"
|
name = "lesavka_common"
|
||||||
version = "0.14.41"
|
version = "0.14.42"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
|
|||||||
@ -9,10 +9,22 @@ log() { printf '[lesavka-core] %s\n' "$*"; }
|
|||||||
|
|
||||||
G=/sys/kernel/config/usb_gadget/lesavka
|
G=/sys/kernel/config/usb_gadget/lesavka
|
||||||
|
|
||||||
if [[ -r /etc/lesavka/uvc.env ]]; then
|
load_uvc_env_defaults() {
|
||||||
# shellcheck disable=SC1091
|
local env_file=/etc/lesavka/uvc.env
|
||||||
source /etc/lesavka/uvc.env
|
[[ -r $env_file ]] || return 0
|
||||||
fi
|
local line key value
|
||||||
|
while IFS= read -r line || [[ -n $line ]]; do
|
||||||
|
[[ $line =~ ^[[:space:]]*# || -z $line ]] && continue
|
||||||
|
[[ $line == *=* ]] || continue
|
||||||
|
key=${line%%=*}
|
||||||
|
value=${line#*=}
|
||||||
|
[[ $key =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]] || continue
|
||||||
|
[[ -z ${!key+x} ]] || continue
|
||||||
|
export "$key=$value"
|
||||||
|
done <"$env_file"
|
||||||
|
}
|
||||||
|
|
||||||
|
load_uvc_env_defaults
|
||||||
|
|
||||||
find_udc() {
|
find_udc() {
|
||||||
ls /sys/class/udc 2>/dev/null | head -n1 || true
|
ls /sys/class/udc 2>/dev/null | head -n1 || true
|
||||||
@ -505,19 +517,7 @@ EOF
|
|||||||
fi
|
fi
|
||||||
popd >/dev/null
|
popd >/dev/null
|
||||||
|
|
||||||
streaming_class_speeds=(fs)
|
for s in fs hs ss; do
|
||||||
control_class_speeds=(fs)
|
|
||||||
case "$MAX_SPEED" in
|
|
||||||
super-speed|super-speed-plus)
|
|
||||||
streaming_class_speeds+=(hs ss)
|
|
||||||
control_class_speeds+=(ss)
|
|
||||||
;;
|
|
||||||
high-speed)
|
|
||||||
streaming_class_speeds+=(hs)
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
for s in "${streaming_class_speeds[@]}"; do
|
|
||||||
mkdir -p "$F/streaming/class/$s"
|
mkdir -p "$F/streaming/class/$s"
|
||||||
pushd "$F/streaming/class/$s" >/dev/null
|
pushd "$F/streaming/class/$s" >/dev/null
|
||||||
ln -s ../../header/h h
|
ln -s ../../header/h h
|
||||||
@ -526,12 +526,11 @@ EOF
|
|||||||
|
|
||||||
# ── 4. Video‑Control interface ─────────────────────────────────────
|
# ── 4. Video‑Control interface ─────────────────────────────────────
|
||||||
mkdir -p "$F/control/header/h"
|
mkdir -p "$F/control/header/h"
|
||||||
# The kernel UVC gadget docs make the control-speed links optional; only
|
# The kernel UVC gadget docs require direct symlinks at control/class/fs and
|
||||||
# advertise the descriptor sets that match the configured gadget speed.
|
# control/class/ss. High-speed control descriptors are not exposed here.
|
||||||
pushd "$F/control" >/dev/null
|
pushd "$F/control" >/dev/null
|
||||||
for s in "${control_class_speeds[@]}"; do
|
ln -s header/h class/fs 2>/dev/null || true
|
||||||
ln -s header/h "class/$s" 2>/dev/null || true
|
ln -s header/h class/ss 2>/dev/null || true
|
||||||
done
|
|
||||||
popd >/dev/null
|
popd >/dev/null
|
||||||
|
|
||||||
if [[ -n $UVC_DISABLE_IRQ ]]; then
|
if [[ -n $UVC_DISABLE_IRQ ]]; then
|
||||||
|
|||||||
@ -2,11 +2,24 @@
|
|||||||
# scripts/daemon/lesavka-uvc.sh - launch UVC control helper as a standalone service
|
# scripts/daemon/lesavka-uvc.sh - launch UVC control helper as a standalone service
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# Optional env file for runtime overrides (debug, width/fps, etc.)
|
# Optional env file for runtime defaults (debug, width/fps, etc.). Explicit
|
||||||
if [[ -r /etc/lesavka/uvc.env ]]; then
|
# environment from a recovery shell wins so bench overrides are not masked.
|
||||||
# shellcheck disable=SC1091
|
load_uvc_env_defaults() {
|
||||||
source /etc/lesavka/uvc.env
|
local env_file=/etc/lesavka/uvc.env
|
||||||
fi
|
[[ -r $env_file ]] || return 0
|
||||||
|
local line key value
|
||||||
|
while IFS= read -r line || [[ -n $line ]]; do
|
||||||
|
[[ $line =~ ^[[:space:]]*# || -z $line ]] && continue
|
||||||
|
[[ $line == *=* ]] || continue
|
||||||
|
key=${line%%=*}
|
||||||
|
value=${line#*=}
|
||||||
|
[[ $key =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]] || continue
|
||||||
|
[[ -z ${!key+x} ]] || continue
|
||||||
|
export "$key=$value"
|
||||||
|
done <"$env_file"
|
||||||
|
}
|
||||||
|
|
||||||
|
load_uvc_env_defaults
|
||||||
|
|
||||||
resolve_default_uvc_dev() {
|
resolve_default_uvc_dev() {
|
||||||
local ctrl=""
|
local ctrl=""
|
||||||
|
|||||||
@ -829,7 +829,8 @@ echo "==> 6b. Systemd units - lesavka-server"
|
|||||||
cat <<'UNIT' | sudo tee /etc/systemd/system/lesavka-server.service >/dev/null
|
cat <<'UNIT' | sudo tee /etc/systemd/system/lesavka-server.service >/dev/null
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=lesavka gRPC relay
|
Description=lesavka gRPC relay
|
||||||
After=network.target lesavka-core.service
|
After=network.target lesavka-core.service lesavka-uvc.service
|
||||||
|
Wants=lesavka-uvc.service
|
||||||
StartLimitIntervalSec=30
|
StartLimitIntervalSec=30
|
||||||
StartLimitBurst=10
|
StartLimitBurst=10
|
||||||
|
|
||||||
@ -850,11 +851,12 @@ Environment=LESAVKA_EYE_FPS=20
|
|||||||
Environment=LESAVKA_MIC_INIT_ATTEMPTS=5
|
Environment=LESAVKA_MIC_INIT_ATTEMPTS=5
|
||||||
Environment=LESAVKA_MIC_INIT_DELAY_MS=250
|
Environment=LESAVKA_MIC_INIT_DELAY_MS=250
|
||||||
Environment=LESAVKA_ALLOW_GADGET_CYCLE=1
|
Environment=LESAVKA_ALLOW_GADGET_CYCLE=1
|
||||||
|
Environment=LESAVKA_SERVER_LOG_PATH=/var/log/lesavka/server.log
|
||||||
EnvironmentFile=-/etc/lesavka/uvc.env
|
EnvironmentFile=-/etc/lesavka/uvc.env
|
||||||
EnvironmentFile=-/etc/lesavka/server.env
|
EnvironmentFile=-/etc/lesavka/server.env
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=5
|
RestartSec=5
|
||||||
StandardError=append:/tmp/lesavka-server.stderr
|
StandardError=append:/var/log/lesavka/server.stderr
|
||||||
User=root
|
User=root
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
@ -862,7 +864,9 @@ WantedBy=multi-user.target
|
|||||||
UNIT
|
UNIT
|
||||||
|
|
||||||
echo "==> 6c. Systemd units - initialization"
|
echo "==> 6c. Systemd units - initialization"
|
||||||
sudo truncate -s 0 /tmp/lesavka-server.log
|
sudo install -d -m 0755 /var/log/lesavka
|
||||||
|
sudo rm -f /tmp/lesavka-server.log
|
||||||
|
sudo truncate -s 0 /var/log/lesavka/server.log
|
||||||
sudo systemctl daemon-reload
|
sudo systemctl daemon-reload
|
||||||
sudo systemctl enable lesavka-core lesavka-server
|
sudo systemctl enable lesavka-core lesavka-server
|
||||||
|
|
||||||
@ -878,6 +882,8 @@ if [[ "$UVC_ENV_CHANGED" == "1" ]] && is_attached_state "$UDC_STATE"; then
|
|||||||
fi
|
fi
|
||||||
if [[ -n ${LESAVKA_ALLOW_GADGET_RESET:-} ]] || [[ "$FORCE_GADGET_REBUILD" == "1" ]] || ! is_attached_state "$UDC_STATE"; then
|
if [[ -n ${LESAVKA_ALLOW_GADGET_RESET:-} ]] || [[ "$FORCE_GADGET_REBUILD" == "1" ]] || ! is_attached_state "$UDC_STATE"; then
|
||||||
echo "⚠️ UDC state is '$UDC_STATE' - forcing a Lesavka gadget rebuild before server start."
|
echo "⚠️ UDC state is '$UDC_STATE' - forcing a Lesavka gadget rebuild before server start."
|
||||||
|
sudo systemctl stop lesavka-uvc >/dev/null 2>&1 || true
|
||||||
|
sudo systemctl reset-failed lesavka-uvc >/dev/null 2>&1 || true
|
||||||
sudo env \
|
sudo env \
|
||||||
LESAVKA_ALLOW_GADGET_RESET=1 \
|
LESAVKA_ALLOW_GADGET_RESET=1 \
|
||||||
LESAVKA_ATTACH_WRITE_UDC=1 \
|
LESAVKA_ATTACH_WRITE_UDC=1 \
|
||||||
@ -906,7 +912,7 @@ RestartSec=2
|
|||||||
KillSignal=SIGTERM
|
KillSignal=SIGTERM
|
||||||
KillMode=control-group
|
KillMode=control-group
|
||||||
TimeoutStopSec=10
|
TimeoutStopSec=10
|
||||||
StandardError=append:/tmp/lesavka-uvc.stderr
|
StandardError=append:/var/log/lesavka/uvc.stderr
|
||||||
User=root
|
User=root
|
||||||
EnvironmentFile=-/etc/lesavka/uvc.env
|
EnvironmentFile=-/etc/lesavka/uvc.env
|
||||||
|
|
||||||
@ -934,12 +940,14 @@ if [[ "$UVC_ENV_CHANGED" == "1" ]] && systemctl is-active --quiet lesavka-uvc; t
|
|||||||
elif systemctl is-active --quiet lesavka-uvc; then
|
elif systemctl is-active --quiet lesavka-uvc; then
|
||||||
echo "✅ lesavka-uvc already active; runtime settings unchanged."
|
echo "✅ lesavka-uvc already active; runtime settings unchanged."
|
||||||
else
|
else
|
||||||
echo "⚠️ lesavka-uvc is not active; start via lesavka-core dependency path."
|
sudo truncate -s 0 /var/log/lesavka/uvc.stderr
|
||||||
|
sudo systemctl start lesavka-uvc
|
||||||
|
echo "✅ lesavka-uvc started to attach the UVC gadget to the host."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
validate_uvc_gadget_ready
|
validate_uvc_gadget_ready
|
||||||
|
|
||||||
sudo truncate -s 0 /tmp/lesavka-server.stderr
|
sudo truncate -s 0 /var/log/lesavka/server.stderr
|
||||||
sudo systemctl stop lesavka-server >/dev/null 2>&1 || true
|
sudo systemctl stop lesavka-server >/dev/null 2>&1 || true
|
||||||
clear_stale_server_listener
|
clear_stale_server_listener
|
||||||
sudo systemctl reset-failed lesavka-server >/dev/null 2>&1 || true
|
sudo systemctl reset-failed lesavka-server >/dev/null 2>&1 || true
|
||||||
|
|||||||
@ -15,7 +15,10 @@ LESAVKA_SERVER_CONNECT_HOST=${LESAVKA_SERVER_CONNECT_HOST:-38.28.125.112}
|
|||||||
LESAVKA_SERVER_ADDR=${LESAVKA_SERVER_ADDR:-auto}
|
LESAVKA_SERVER_ADDR=${LESAVKA_SERVER_ADDR:-auto}
|
||||||
PROBE_DURATION_SECONDS=${PROBE_DURATION_SECONDS:-10}
|
PROBE_DURATION_SECONDS=${PROBE_DURATION_SECONDS:-10}
|
||||||
PROBE_WARMUP_SECONDS=${PROBE_WARMUP_SECONDS:-4}
|
PROBE_WARMUP_SECONDS=${PROBE_WARMUP_SECONDS:-4}
|
||||||
LEAD_IN_SECONDS=${LEAD_IN_SECONDS:-8}
|
# Do not open the UVC host capture far ahead of the probe. The gadget side only
|
||||||
|
# has frames once the sync probe is feeding the server, and some hosts time out
|
||||||
|
# VIDIOC_STREAMON if the camera is starved during pre-roll.
|
||||||
|
LEAD_IN_SECONDS=${LEAD_IN_SECONDS:-0}
|
||||||
TAIL_SECONDS=${TAIL_SECONDS:-2}
|
TAIL_SECONDS=${TAIL_SECONDS:-2}
|
||||||
CAPTURE_SECONDS=${CAPTURE_SECONDS:-$((PROBE_DURATION_SECONDS + PROBE_WARMUP_SECONDS + LEAD_IN_SECONDS + TAIL_SECONDS))}
|
CAPTURE_SECONDS=${CAPTURE_SECONDS:-$((PROBE_DURATION_SECONDS + PROBE_WARMUP_SECONDS + LEAD_IN_SECONDS + TAIL_SECONDS))}
|
||||||
REMOTE_CAPTURE=${REMOTE_CAPTURE:-/tmp/lesavka-upstream-av-sync.mkv}
|
REMOTE_CAPTURE=${REMOTE_CAPTURE:-/tmp/lesavka-upstream-av-sync.mkv}
|
||||||
@ -773,6 +776,11 @@ if [[ -f "${LOCAL_CAPTURE_LOG}" ]] \
|
|||||||
&& grep -q 'VIDIOC_QBUF): Bad file descriptor' "${LOCAL_CAPTURE_LOG}"; then
|
&& grep -q 'VIDIOC_QBUF): Bad file descriptor' "${LOCAL_CAPTURE_LOG}"; then
|
||||||
capture_v4l2_fault=1
|
capture_v4l2_fault=1
|
||||||
fi
|
fi
|
||||||
|
capture_streamon_timeout=0
|
||||||
|
if [[ -f "${LOCAL_CAPTURE_LOG}" ]] \
|
||||||
|
&& grep -q 'VIDIOC_STREAMON.*Connection timed out' "${LOCAL_CAPTURE_LOG}"; then
|
||||||
|
capture_streamon_timeout=1
|
||||||
|
fi
|
||||||
|
|
||||||
if ssh ${SSH_OPTS} "${TETHYS_HOST}" "test -f '${REMOTE_CAPTURE}'"; then
|
if ssh ${SSH_OPTS} "${TETHYS_HOST}" "test -f '${REMOTE_CAPTURE}'"; then
|
||||||
remote_fetch_capture="${REMOTE_CAPTURE}"
|
remote_fetch_capture="${REMOTE_CAPTURE}"
|
||||||
@ -826,6 +834,10 @@ if [[ "${probe_status}" -ne 0 ]]; then
|
|||||||
exit "${probe_status}"
|
exit "${probe_status}"
|
||||||
fi
|
fi
|
||||||
if [[ "${capture_status}" -ne 0 ]]; then
|
if [[ "${capture_status}" -ne 0 ]]; then
|
||||||
|
if [[ "${capture_streamon_timeout}" -eq 1 ]]; then
|
||||||
|
echo "Tethys capture timed out during VIDIOC_STREAMON; the UVC host opened before MJPEG frames reached the gadget." >&2
|
||||||
|
echo "Keep LEAD_IN_SECONDS=0 and restart lesavka-uvc/lesavka-server before retrying if the gadget is wedged from an earlier failed run." >&2
|
||||||
|
fi
|
||||||
if [[ "${capture_status}" -eq 141 && ( -f "${LOCAL_CAPTURE}" || -f "${LOCAL_ANALYSIS_JSON}" ) ]]; then
|
if [[ "${capture_status}" -eq 141 && ( -f "${LOCAL_CAPTURE}" || -f "${LOCAL_ANALYSIS_JSON}" ) ]]; then
|
||||||
echo "Tethys capture ended with PipeWire SIGPIPE after ffmpeg closed; accepting preserved analysis artifacts" >&2
|
echo "Tethys capture ended with PipeWire SIGPIPE after ffmpeg closed; accepting preserved analysis artifacts" >&2
|
||||||
elif [[ "${capture_status}" -eq 124 && ( -f "${LOCAL_CAPTURE}" || -f "${LOCAL_ANALYSIS_JSON}" ) ]]; then
|
elif [[ "${capture_status}" -eq 124 && ( -f "${LOCAL_CAPTURE}" || -f "${LOCAL_ANALYSIS_JSON}" ) ]]; then
|
||||||
|
|||||||
@ -10,7 +10,7 @@ bench = false
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "lesavka_server"
|
name = "lesavka_server"
|
||||||
version = "0.14.41"
|
version = "0.14.42"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
autobins = false
|
autobins = false
|
||||||
|
|
||||||
|
|||||||
@ -29,32 +29,66 @@ pub fn init_tracing() -> anyhow::Result<WorkerGuard> {
|
|||||||
|
|
||||||
#[cfg(not(coverage))]
|
#[cfg(not(coverage))]
|
||||||
pub fn init_tracing() -> anyhow::Result<WorkerGuard> {
|
pub fn init_tracing() -> anyhow::Result<WorkerGuard> {
|
||||||
let file = std::fs::OpenOptions::new()
|
|
||||||
.create(true)
|
|
||||||
.truncate(true)
|
|
||||||
.write(true)
|
|
||||||
.open("/tmp/lesavka-server.log")?;
|
|
||||||
let (file_writer, guard) = tracing_appender::non_blocking(file);
|
|
||||||
|
|
||||||
let env_filter = EnvFilter::try_from_default_env()
|
let env_filter = EnvFilter::try_from_default_env()
|
||||||
.unwrap_or_else(|_| EnvFilter::new("lesavka_server=info,lesavka_server::video=warn"));
|
.unwrap_or_else(|_| EnvFilter::new("lesavka_server=info,lesavka_server::video=warn"));
|
||||||
let filter_str = env_filter.to_string();
|
let filter_str = env_filter.to_string();
|
||||||
|
let file = open_server_log_file();
|
||||||
|
let log_open_error = file.as_ref().err().map(ToString::to_string);
|
||||||
|
let (file_writer, guard) = match file {
|
||||||
|
Ok(file) => {
|
||||||
|
let (writer, guard) = tracing_appender::non_blocking(file);
|
||||||
|
(Some(writer), guard)
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
let (_writer, guard) = tracing_appender::non_blocking(std::io::sink());
|
||||||
|
(None, guard)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
tracing_subscriber::registry()
|
let registry = tracing_subscriber::registry()
|
||||||
.with(env_filter)
|
.with(env_filter)
|
||||||
.with(fmt::layer().with_target(true).with_thread_ids(true))
|
.with(fmt::layer().with_target(true).with_thread_ids(true));
|
||||||
.with(
|
if let Some(file_writer) = file_writer {
|
||||||
fmt::layer()
|
registry
|
||||||
.with_writer(file_writer)
|
.with(
|
||||||
.with_ansi(false)
|
fmt::layer()
|
||||||
.with_target(true)
|
.with_writer(file_writer)
|
||||||
.with_level(true),
|
.with_ansi(false)
|
||||||
)
|
.with_target(true)
|
||||||
.init();
|
.with_level(true),
|
||||||
|
)
|
||||||
|
.init();
|
||||||
|
} else {
|
||||||
|
registry.init();
|
||||||
|
}
|
||||||
tracing::info!("📜 effective RUST_LOG = \"{}\"", filter_str);
|
tracing::info!("📜 effective RUST_LOG = \"{}\"", filter_str);
|
||||||
|
if let Some(error) = log_open_error {
|
||||||
|
tracing::warn!("file logging disabled: {error}");
|
||||||
|
}
|
||||||
Ok(guard)
|
Ok(guard)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(coverage))]
|
||||||
|
fn open_server_log_file() -> std::io::Result<std::fs::File> {
|
||||||
|
let preferred = std::env::var("LESAVKA_SERVER_LOG_PATH")
|
||||||
|
.unwrap_or_else(|_| "/var/log/lesavka/server.log".to_string());
|
||||||
|
for path in [preferred.as_str(), "/tmp/lesavka-server.log"] {
|
||||||
|
match std::fs::OpenOptions::new()
|
||||||
|
.create(true)
|
||||||
|
.truncate(true)
|
||||||
|
.write(true)
|
||||||
|
.open(path)
|
||||||
|
{
|
||||||
|
Ok(file) => return Ok(file),
|
||||||
|
Err(error) if path != "/tmp/lesavka-server.log" => {
|
||||||
|
eprintln!("lesavka-server: failed to open {path}: {error}; trying /tmp fallback");
|
||||||
|
}
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unreachable!("static log path list is non-empty")
|
||||||
|
}
|
||||||
|
|
||||||
/// Open a HID gadget endpoint with bounded retry logic.
|
/// Open a HID gadget endpoint with bounded retry logic.
|
||||||
///
|
///
|
||||||
/// Inputs: the path of the gadget device node to open.
|
/// Inputs: the path of the gadget device node to open.
|
||||||
|
|||||||
@ -19,6 +19,9 @@ fn upstream_sync_script_tunnels_auto_server_addr_through_ssh() {
|
|||||||
"RESOLVED_LESAVKA_SERVER_ADDR=\"http://127.0.0.1:${SERVER_TUNNEL_LOCAL_PORT}\"",
|
"RESOLVED_LESAVKA_SERVER_ADDR=\"http://127.0.0.1:${SERVER_TUNNEL_LOCAL_PORT}\"",
|
||||||
"tunneled to ${LESAVKA_SERVER_HOST}:127.0.0.1:${SERVER_TUNNEL_REMOTE_PORT}",
|
"tunneled to ${LESAVKA_SERVER_HOST}:127.0.0.1:${SERVER_TUNNEL_REMOTE_PORT}",
|
||||||
"CAPTURE_READY_MARKER=\"__LESAVKA_CAPTURE_READY__\"",
|
"CAPTURE_READY_MARKER=\"__LESAVKA_CAPTURE_READY__\"",
|
||||||
|
"LEAD_IN_SECONDS=${LEAD_IN_SECONDS:-0}",
|
||||||
|
"VIDIOC_STREAMON.*Connection timed out",
|
||||||
|
"the UVC host opened before MJPEG frames reached the gadget",
|
||||||
"Tethys capture failed before the sync probe could start",
|
"Tethys capture failed before the sync probe could start",
|
||||||
"wait_for_capture_ready",
|
"wait_for_capture_ready",
|
||||||
"Timed out waiting for Tethys capture to become ready",
|
"Timed out waiting for Tethys capture to become ready",
|
||||||
@ -34,7 +37,9 @@ fn upstream_sync_script_tunnels_auto_server_addr_through_ssh() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
assert!(
|
assert!(
|
||||||
!SYNC_SCRIPT.contains("RESOLVED_LESAVKA_SERVER_ADDR=\"http://${LESAVKA_SERVER_CONNECT_HOST}:${port}\""),
|
!SYNC_SCRIPT.contains(
|
||||||
|
"RESOLVED_LESAVKA_SERVER_ADDR=\"http://${LESAVKA_SERVER_CONNECT_HOST}:${port}\""
|
||||||
|
),
|
||||||
"auto server resolution should not guess a public gRPC host when SSH is already required"
|
"auto server resolution should not guess a public gRPC host when SSH is already required"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,9 +43,8 @@ fn core_script_skips_soft_connect_for_dwc2() {
|
|||||||
fn core_script_uses_kernel_doc_control_header_links() {
|
fn core_script_uses_kernel_doc_control_header_links() {
|
||||||
for expected in [
|
for expected in [
|
||||||
"pushd \"$F/control\" >/dev/null",
|
"pushd \"$F/control\" >/dev/null",
|
||||||
"ln -s header/h \"class/$s\"",
|
"ln -s header/h class/fs",
|
||||||
"control_class_speeds=(fs)",
|
"ln -s header/h class/ss",
|
||||||
"for s in \"${control_class_speeds[@]}\"; do",
|
|
||||||
] {
|
] {
|
||||||
assert!(
|
assert!(
|
||||||
CORE_SCRIPT.contains(expected),
|
CORE_SCRIPT.contains(expected),
|
||||||
@ -59,21 +58,31 @@ fn core_script_uses_kernel_doc_control_header_links() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn core_script_matches_uvc_descriptor_speeds_to_max_speed() {
|
fn core_script_keeps_known_good_uvc_descriptor_links() {
|
||||||
for expected in [
|
for expected in ["for s in fs hs ss; do", "ln -s ../../header/h h"] {
|
||||||
"streaming_class_speeds=(fs)",
|
|
||||||
"control_class_speeds=(fs)",
|
|
||||||
"case \"$MAX_SPEED\" in",
|
|
||||||
"super-speed|super-speed-plus)",
|
|
||||||
"streaming_class_speeds+=(hs ss)",
|
|
||||||
"control_class_speeds+=(ss)",
|
|
||||||
"high-speed)",
|
|
||||||
"streaming_class_speeds+=(hs)",
|
|
||||||
"for s in \"${streaming_class_speeds[@]}\"; do",
|
|
||||||
] {
|
|
||||||
assert!(
|
assert!(
|
||||||
CORE_SCRIPT.contains(expected),
|
CORE_SCRIPT.contains(expected),
|
||||||
"lesavka-core speed guard missing: {expected}"
|
"lesavka-core descriptor guard missing: {expected}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for unexpected in ["streaming_class_speeds", "control_class_speeds"] {
|
||||||
|
assert!(
|
||||||
|
!CORE_SCRIPT.contains(unexpected),
|
||||||
|
"lesavka-core should not trim UVC descriptor links by gadget speed: {unexpected}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn core_script_treats_uvc_env_as_defaults_not_overrides() {
|
||||||
|
for expected in [
|
||||||
|
"load_uvc_env_defaults()",
|
||||||
|
"[[ -z ${!key+x} ]] || continue",
|
||||||
|
"export \"$key=$value\"",
|
||||||
|
] {
|
||||||
|
assert!(
|
||||||
|
CORE_SCRIPT.contains(expected),
|
||||||
|
"lesavka-core env-default guard missing: {expected}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -57,7 +57,8 @@ fn server_install_pins_hdmi_camera_and_display_defaults() {
|
|||||||
"install script should not let ambient LESAVKA_UVC_CODEC leak into persisted defaults"
|
"install script should not let ambient LESAVKA_UVC_CODEC leak into persisted defaults"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
!SERVER_INSTALL.contains("LESAVKA_SERVER_BIND_ADDR=${LESAVKA_SERVER_BIND_ADDR:-0.0.0.0:50051}"),
|
!SERVER_INSTALL
|
||||||
|
.contains("LESAVKA_SERVER_BIND_ADDR=${LESAVKA_SERVER_BIND_ADDR:-0.0.0.0:50051}"),
|
||||||
"install script should not let ambient LESAVKA_SERVER_BIND_ADDR leak into persisted defaults"
|
"install script should not let ambient LESAVKA_SERVER_BIND_ADDR leak into persisted defaults"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
@ -81,13 +82,19 @@ fn server_install_pins_hdmi_camera_and_display_defaults() {
|
|||||||
"install script should detect when the live gadget is missing the expected UVC function"
|
"install script should detect when the live gadget is missing the expected UVC function"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
SERVER_INSTALL.contains("UVC function is missing from the live gadget; forcing a rebuild before server start."),
|
SERVER_INSTALL.contains(
|
||||||
|
"UVC function is missing from the live gadget; forcing a rebuild before server start."
|
||||||
|
),
|
||||||
"install script should force a rebuild when the live gadget is attached but missing UVC"
|
"install script should force a rebuild when the live gadget is attached but missing UVC"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
SERVER_INSTALL.contains("lesavka-core gadget rebuilt directly."),
|
SERVER_INSTALL.contains("lesavka-core gadget rebuilt directly."),
|
||||||
"install script should trust the direct forced rebuild instead of immediately rerunning the oneshot core unit"
|
"install script should trust the direct forced rebuild instead of immediately rerunning the oneshot core unit"
|
||||||
);
|
);
|
||||||
|
assert!(
|
||||||
|
SERVER_INSTALL.contains("sudo systemctl stop lesavka-uvc"),
|
||||||
|
"install script should stop the UVC helper before directly rebuilding the gadget underneath it"
|
||||||
|
);
|
||||||
assert!(
|
assert!(
|
||||||
!SERVER_INSTALL.contains("sudo systemctl restart lesavka-core"),
|
!SERVER_INSTALL.contains("sudo systemctl restart lesavka-core"),
|
||||||
"install script should not immediately rerun lesavka-core after a successful direct forced rebuild"
|
"install script should not immediately rerun lesavka-core after a successful direct forced rebuild"
|
||||||
@ -112,6 +119,26 @@ fn server_install_pins_hdmi_camera_and_display_defaults() {
|
|||||||
SERVER_INSTALL.contains("lesavka-uvc already active; runtime settings unchanged."),
|
SERVER_INSTALL.contains("lesavka-uvc already active; runtime settings unchanged."),
|
||||||
"install script should avoid unnecessary UVC restarts when nothing changed"
|
"install script should avoid unnecessary UVC restarts when nothing changed"
|
||||||
);
|
);
|
||||||
|
assert!(
|
||||||
|
SERVER_INSTALL.contains("sudo systemctl start lesavka-uvc"),
|
||||||
|
"install script should start the UVC helper so the host enumerates the UVC function"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
SERVER_INSTALL.contains("lesavka-uvc started to attach the UVC gadget to the host."),
|
||||||
|
"install script should report when it starts the UVC helper for enumeration"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
SERVER_INSTALL.contains("Wants=lesavka-uvc.service"),
|
||||||
|
"server unit should pull in the external UVC helper on UVC installs"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
SERVER_INSTALL.contains("/var/log/lesavka/server.log"),
|
||||||
|
"install script should keep server logs out of sticky /tmp"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
SERVER_INSTALL.contains("LESAVKA_SERVER_LOG_PATH=/var/log/lesavka/server.log"),
|
||||||
|
"server unit should point tracing at the non-sticky log path"
|
||||||
|
);
|
||||||
assert!(
|
assert!(
|
||||||
SERVER_INSTALL.contains("clear_stale_server_listener"),
|
SERVER_INSTALL.contains("clear_stale_server_listener"),
|
||||||
"install script should clear stale server listeners before restart"
|
"install script should clear stale server listeners before restart"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user