fix(uvc): keep control helper off stream queue
This commit is contained in:
parent
5eb984ce08
commit
ce8f855db2
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -1642,7 +1642,7 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||
|
||||
[[package]]
|
||||
name = "lesavka_client"
|
||||
version = "0.14.43"
|
||||
version = "0.14.44"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-stream",
|
||||
@ -1676,7 +1676,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lesavka_common"
|
||||
version = "0.14.43"
|
||||
version = "0.14.44"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
@ -1688,7 +1688,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lesavka_server"
|
||||
version = "0.14.43"
|
||||
version = "0.14.44"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
|
||||
@ -4,7 +4,7 @@ path = "src/main.rs"
|
||||
|
||||
[package]
|
||||
name = "lesavka_client"
|
||||
version = "0.14.43"
|
||||
version = "0.14.44"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "lesavka_common"
|
||||
version = "0.14.43"
|
||||
version = "0.14.44"
|
||||
edition = "2024"
|
||||
build = "build.rs"
|
||||
|
||||
|
||||
@ -36,6 +36,7 @@ LESAVKA_UVC_WIDTH=${LESAVKA_UVC_WIDTH:-640}
|
||||
LESAVKA_UVC_HEIGHT=${LESAVKA_UVC_HEIGHT:-480}
|
||||
LESAVKA_UVC_CODEC=${INSTALL_UVC_CODEC}
|
||||
LESAVKA_UVC_BLOCKING=${LESAVKA_UVC_BLOCKING:-1}
|
||||
LESAVKA_UVC_CONTROL_READ_ONLY=${LESAVKA_UVC_CONTROL_READ_ONLY:-1}
|
||||
LESAVKA_UVC_MAXBURST=${LESAVKA_UVC_MAXBURST:-0}
|
||||
EOF
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ bench = false
|
||||
|
||||
[package]
|
||||
name = "lesavka_server"
|
||||
version = "0.14.43"
|
||||
version = "0.14.44"
|
||||
edition = "2024"
|
||||
autobins = false
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use std::env;
|
||||
use std::fs::OpenOptions;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::thread;
|
||||
@ -135,6 +135,7 @@ struct ConfigfsSnapshot {
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let (dev, cfg) = parse_args()?;
|
||||
let _singleton = acquire_singleton_lock()?;
|
||||
let interfaces = load_interfaces();
|
||||
eprintln!("[lesavka-uvc] starting (dev={dev})");
|
||||
eprintln!(
|
||||
@ -425,15 +426,20 @@ fn read_interface(path: &str) -> Option<u8> {
|
||||
|
||||
fn open_with_retry(path: &str) -> Result<std::fs::File> {
|
||||
let nonblock = env::var("LESAVKA_UVC_BLOCKING").is_err();
|
||||
let read_only = uvc_control_read_only();
|
||||
for attempt in 1..=200 {
|
||||
let mut opts = OpenOptions::new();
|
||||
opts.read(true).write(true);
|
||||
opts.read(true);
|
||||
if !read_only {
|
||||
opts.write(true);
|
||||
}
|
||||
if nonblock {
|
||||
opts.custom_flags(libc::O_NONBLOCK);
|
||||
}
|
||||
match opts.open(path) {
|
||||
Ok(f) => {
|
||||
eprintln!("[lesavka-uvc] opened {path} (attempt {attempt})");
|
||||
let mode = if read_only { "ro" } else { "rw" };
|
||||
eprintln!("[lesavka-uvc] opened {path} mode={mode} (attempt {attempt})");
|
||||
return Ok(f);
|
||||
}
|
||||
Err(err) if err.raw_os_error() == Some(libc::ENOENT) => {
|
||||
@ -445,6 +451,42 @@ fn open_with_retry(path: &str) -> Result<std::fs::File> {
|
||||
Err(anyhow::anyhow!("timeout opening {path}"))
|
||||
}
|
||||
|
||||
fn uvc_control_read_only() -> bool {
|
||||
env::var("LESAVKA_UVC_CONTROL_READ_ONLY")
|
||||
.ok()
|
||||
.map(|value| {
|
||||
let trimmed = value.trim();
|
||||
!(trimmed.eq_ignore_ascii_case("0")
|
||||
|| trimmed.eq_ignore_ascii_case("false")
|
||||
|| trimmed.eq_ignore_ascii_case("no")
|
||||
|| trimmed.eq_ignore_ascii_case("off"))
|
||||
})
|
||||
.unwrap_or(true)
|
||||
}
|
||||
|
||||
fn acquire_singleton_lock() -> Result<File> {
|
||||
let path =
|
||||
env::var("LESAVKA_UVC_LOCK_PATH").unwrap_or_else(|_| "/run/lesavka-uvc.lock".to_string());
|
||||
let file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(&path)
|
||||
.with_context(|| format!("open singleton lock {path}"))?;
|
||||
let rc = unsafe { libc::flock(file.as_raw_fd(), libc::LOCK_EX | libc::LOCK_NB) };
|
||||
if rc < 0 {
|
||||
let err = std::io::Error::last_os_error();
|
||||
match err.raw_os_error() {
|
||||
Some(code) if code == libc::EWOULDBLOCK || code == libc::EAGAIN => {
|
||||
eprintln!("[lesavka-uvc] another helper already owns {path}; exiting");
|
||||
std::process::exit(0);
|
||||
}
|
||||
_ => return Err(err).with_context(|| format!("lock singleton {path}")),
|
||||
}
|
||||
}
|
||||
Ok(file)
|
||||
}
|
||||
|
||||
fn subscribe_event(fd: i32, req: libc::c_ulong, event: u32) -> Result<()> {
|
||||
let mut sub = V4l2EventSubscription {
|
||||
type_: event,
|
||||
@ -951,14 +993,15 @@ fn compute_payload_cap(bulk: bool) -> Option<PayloadCap> {
|
||||
let mut non_periodic =
|
||||
read_fifo_min("/sys/module/dwc2/parameters/g_np_tx_fifo_size").map(|v| (v, "dwc2.params"));
|
||||
if (periodic.is_none() || non_periodic.is_none())
|
||||
&& let Some((p, np)) = read_debugfs_fifos() {
|
||||
if periodic.is_none() {
|
||||
periodic = p.map(|v| (v, "debugfs.params"));
|
||||
}
|
||||
if non_periodic.is_none() {
|
||||
non_periodic = np.map(|v| (v, "debugfs.params"));
|
||||
}
|
||||
&& let Some((p, np)) = read_debugfs_fifos()
|
||||
{
|
||||
if periodic.is_none() {
|
||||
periodic = p.map(|v| (v, "debugfs.params"));
|
||||
}
|
||||
if non_periodic.is_none() {
|
||||
non_periodic = np.map(|v| (v, "debugfs.params"));
|
||||
}
|
||||
}
|
||||
let periodic_dw = periodic.map(|(v, _)| v);
|
||||
let non_periodic_dw = non_periodic.map(|(v, _)| v);
|
||||
|
||||
|
||||
@ -30,6 +30,7 @@ fn server_install_pins_hdmi_camera_and_display_defaults() {
|
||||
"LESAVKA_UVC_WIDTH=",
|
||||
"LESAVKA_UVC_HEIGHT=",
|
||||
"LESAVKA_UVC_CODEC=",
|
||||
"LESAVKA_UVC_CONTROL_READ_ONLY=",
|
||||
] {
|
||||
assert!(
|
||||
SERVER_INSTALL.contains(expected),
|
||||
@ -52,6 +53,7 @@ fn server_install_pins_hdmi_camera_and_display_defaults() {
|
||||
assert!(SERVER_INSTALL.contains("${LESAVKA_UVC_INTERVAL:-500000}"));
|
||||
assert!(SERVER_INSTALL.contains("${LESAVKA_UVC_WIDTH:-640}"));
|
||||
assert!(SERVER_INSTALL.contains("${LESAVKA_UVC_HEIGHT:-480}"));
|
||||
assert!(SERVER_INSTALL.contains("${LESAVKA_UVC_CONTROL_READ_ONLY:-1}"));
|
||||
assert!(
|
||||
!SERVER_INSTALL.contains("LESAVKA_UVC_CODEC=${LESAVKA_UVC_CODEC:-mjpeg}"),
|
||||
"install script should not let ambient LESAVKA_UVC_CODEC leak into persisted defaults"
|
||||
|
||||
@ -311,6 +311,22 @@ mod uvc_binary {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn uvc_control_open_mode_defaults_read_only_with_escape_hatch() {
|
||||
with_var("LESAVKA_UVC_CONTROL_READ_ONLY", None::<&str>, || {
|
||||
assert!(uvc_control_read_only());
|
||||
});
|
||||
for disabled in ["0", "false", "no", "off"] {
|
||||
with_var("LESAVKA_UVC_CONTROL_READ_ONLY", Some(disabled), || {
|
||||
assert!(!uvc_control_read_only());
|
||||
});
|
||||
}
|
||||
with_var("LESAVKA_UVC_CONTROL_READ_ONLY", Some("1"), || {
|
||||
assert!(uvc_control_read_only());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interface_helpers_and_configfs_snapshot_are_stable_without_sysfs() {
|
||||
let tmp = NamedTempFile::new().expect("tmp");
|
||||
|
||||
@ -73,9 +73,11 @@ fn uvc_binary_applies_env_config_and_fails_fast_on_non_v4l2_node() {
|
||||
};
|
||||
|
||||
let fake_device = NamedTempFile::new().expect("temp device");
|
||||
let lock = NamedTempFile::new().expect("temp lock");
|
||||
let child = Command::new(Path::new(&bin))
|
||||
.arg("--device")
|
||||
.arg(fake_device.path())
|
||||
.env("LESAVKA_UVC_LOCK_PATH", lock.path())
|
||||
.env("LESAVKA_UVC_MAXPAYLOAD_LIMIT", "256")
|
||||
.env("LESAVKA_UVC_MAXPACKET", "4096")
|
||||
.env("LESAVKA_UVC_BULK", "1")
|
||||
@ -98,8 +100,10 @@ fn uvc_binary_accepts_positional_device_argument() {
|
||||
};
|
||||
|
||||
let fake_device = NamedTempFile::new().expect("temp device");
|
||||
let lock = NamedTempFile::new().expect("temp lock");
|
||||
let child = Command::new(Path::new(&bin))
|
||||
.arg(fake_device.path())
|
||||
.env("LESAVKA_UVC_LOCK_PATH", lock.path())
|
||||
.env_remove("LESAVKA_UVC_BULK")
|
||||
.env("LESAVKA_UVC_MAXPAYLOAD_LIMIT", "2048")
|
||||
.env("LESAVKA_UVC_MAXPACKET", "1024")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user