uvc: cap payload to fifo and fix SET_CUR
This commit is contained in:
parent
c7ec6caf4d
commit
b6479acf11
@ -118,6 +118,79 @@ UVC_FPS=${LESAVKA_UVC_FPS:-25}
|
||||
UVC_DISABLE_IRQ=${LESAVKA_UVC_DISABLE_IRQ:-}
|
||||
UVC_BULK=${LESAVKA_UVC_BULK:-}
|
||||
UVC_CODEC=${LESAVKA_UVC_CODEC:-yuyv}
|
||||
|
||||
uvc_fifo_min() {
|
||||
local path="$1"
|
||||
local raw=""
|
||||
raw="$(cat "$path" 2>/dev/null || true)"
|
||||
if [[ -z $raw ]]; then
|
||||
return 0
|
||||
fi
|
||||
echo "$raw" | tr ', ' '\n' | awk 'NF{print $1}' | awk '
|
||||
$1 > 0 { if (min == "" || $1 < min) min = $1 }
|
||||
END { if (min != "") print min }'
|
||||
}
|
||||
|
||||
compute_uvc_payload_cap() {
|
||||
UVC_PAYLOAD_CAP=""
|
||||
UVC_PAYLOAD_SRC=""
|
||||
UVC_PAYLOAD_PCT=""
|
||||
UVC_FIFO_PERIODIC="$(uvc_fifo_min /sys/module/dwc2/parameters/g_tx_fifo_size)"
|
||||
UVC_FIFO_NP="$(uvc_fifo_min /sys/module/dwc2/parameters/g_np_tx_fifo_size)"
|
||||
|
||||
if [[ -n ${LESAVKA_UVC_MAXPAYLOAD_LIMIT:-} ]]; then
|
||||
UVC_PAYLOAD_CAP="${LESAVKA_UVC_MAXPAYLOAD_LIMIT}"
|
||||
UVC_PAYLOAD_SRC="env"
|
||||
UVC_PAYLOAD_PCT=100
|
||||
return
|
||||
fi
|
||||
|
||||
local chosen=""
|
||||
if [[ -n $UVC_BULK ]]; then
|
||||
if [[ -n $UVC_FIFO_NP ]]; then
|
||||
chosen="$UVC_FIFO_NP"
|
||||
UVC_PAYLOAD_SRC="dwc2.g_np_tx_fifo_size"
|
||||
elif [[ -n $UVC_FIFO_PERIODIC ]]; then
|
||||
chosen="$UVC_FIFO_PERIODIC"
|
||||
UVC_PAYLOAD_SRC="dwc2.g_tx_fifo_size"
|
||||
fi
|
||||
else
|
||||
if [[ -n $UVC_FIFO_PERIODIC ]]; then
|
||||
chosen="$UVC_FIFO_PERIODIC"
|
||||
UVC_PAYLOAD_SRC="dwc2.g_tx_fifo_size"
|
||||
elif [[ -n $UVC_FIFO_NP ]]; then
|
||||
chosen="$UVC_FIFO_NP"
|
||||
UVC_PAYLOAD_SRC="dwc2.g_np_tx_fifo_size"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z $chosen ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
local pct=${LESAVKA_UVC_LIMIT_PCT:-95}
|
||||
if ((pct < 1)); then
|
||||
pct=1
|
||||
elif ((pct > 100)); then
|
||||
pct=100
|
||||
fi
|
||||
UVC_PAYLOAD_PCT=$pct
|
||||
local bytes=$((chosen * 4))
|
||||
UVC_PAYLOAD_CAP=$((bytes * pct / 100))
|
||||
}
|
||||
|
||||
compute_uvc_payload_cap
|
||||
if [[ -n $UVC_PAYLOAD_CAP && $UVC_PAYLOAD_CAP -gt 0 ]]; then
|
||||
log "UVC fifo periodic=${UVC_FIFO_PERIODIC:-?} np=${UVC_FIFO_NP:-?} cap=${UVC_PAYLOAD_CAP}B pct=${UVC_PAYLOAD_PCT:-?} src=${UVC_PAYLOAD_SRC:-?}"
|
||||
if ((UVC_MAXPACKET > UVC_PAYLOAD_CAP)); then
|
||||
log "clamping UVC maxpacket $UVC_MAXPACKET -> $UVC_PAYLOAD_CAP"
|
||||
UVC_MAXPACKET=$UVC_PAYLOAD_CAP
|
||||
fi
|
||||
fi
|
||||
if [[ -n $UVC_BULK && $UVC_MAXPACKET -gt 512 ]]; then
|
||||
log "clamping UVC maxpacket $UVC_MAXPACKET -> 512 (bulk)"
|
||||
UVC_MAXPACKET=512
|
||||
fi
|
||||
if [[ -n ${LESAVKA_UVC_MJPEG:-} ]]; then
|
||||
UVC_CODEC=mjpeg
|
||||
fi
|
||||
|
||||
@ -91,6 +91,14 @@ struct UvcConfig {
|
||||
frame_size: u32,
|
||||
}
|
||||
|
||||
struct PayloadCap {
|
||||
limit: u32,
|
||||
pct: u32,
|
||||
source: &'static str,
|
||||
periodic_dw: Option<u32>,
|
||||
non_periodic_dw: Option<u32>,
|
||||
}
|
||||
|
||||
struct UvcState {
|
||||
cfg: UvcConfig,
|
||||
ctrl_len: usize,
|
||||
@ -103,6 +111,7 @@ struct UvcState {
|
||||
struct PendingRequest {
|
||||
interface: u8,
|
||||
selector: u8,
|
||||
expected_len: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
@ -261,6 +270,32 @@ impl UvcConfig {
|
||||
let interval = env_u32("LESAVKA_UVC_INTERVAL", 0);
|
||||
let mut max_packet = env_u32("LESAVKA_UVC_MAXPACKET", 1024);
|
||||
let frame_size = env_u32("LESAVKA_UVC_FRAME_SIZE", width * height * 2);
|
||||
let bulk = env::var("LESAVKA_UVC_BULK").is_ok();
|
||||
if let Some(cap) = compute_payload_cap(bulk) {
|
||||
if max_packet > cap.limit {
|
||||
eprintln!(
|
||||
"[lesavka-uvc] payload cap {}B ({}% from {}): clamp max_packet {} -> {} (periodic_dw={:?} non_periodic_dw={:?})",
|
||||
cap.limit,
|
||||
cap.pct,
|
||||
cap.source,
|
||||
max_packet,
|
||||
cap.limit,
|
||||
cap.periodic_dw,
|
||||
cap.non_periodic_dw
|
||||
);
|
||||
max_packet = cap.limit;
|
||||
} else {
|
||||
eprintln!(
|
||||
"[lesavka-uvc] payload cap {}B ({}% from {}): max_packet {} (periodic_dw={:?} non_periodic_dw={:?})",
|
||||
cap.limit,
|
||||
cap.pct,
|
||||
cap.source,
|
||||
max_packet,
|
||||
cap.periodic_dw,
|
||||
cap.non_periodic_dw
|
||||
);
|
||||
}
|
||||
}
|
||||
if env::var("LESAVKA_UVC_BULK").is_ok() {
|
||||
max_packet = max_packet.min(512);
|
||||
} else {
|
||||
@ -383,9 +418,21 @@ fn handle_setup(
|
||||
let _ = send_stall(fd, uvc_send_response);
|
||||
return;
|
||||
}
|
||||
*pending = Some(PendingRequest { interface, selector });
|
||||
let len = req.w_length as usize;
|
||||
let payload = vec![0u8; len.min(UVC_DATA_SIZE)];
|
||||
if len > UVC_DATA_SIZE {
|
||||
eprintln!(
|
||||
"[lesavka-uvc] SET_CUR too large len={} (max={}); stalling",
|
||||
len, UVC_DATA_SIZE
|
||||
);
|
||||
let _ = send_stall(fd, uvc_send_response);
|
||||
return;
|
||||
}
|
||||
*pending = Some(PendingRequest {
|
||||
interface,
|
||||
selector,
|
||||
expected_len: len,
|
||||
});
|
||||
let payload = vec![0u8; len];
|
||||
let _ = send_response(fd, uvc_send_response, &payload);
|
||||
if debug {
|
||||
eprintln!(
|
||||
@ -477,6 +524,9 @@ fn handle_data(
|
||||
debug: bool,
|
||||
) {
|
||||
let Some(p) = pending.take() else {
|
||||
if debug {
|
||||
eprintln!("[lesavka-uvc] DATA with no pending request; ignoring");
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
@ -485,6 +535,12 @@ fn handle_data(
|
||||
}
|
||||
|
||||
let len = data.length as usize;
|
||||
if debug && p.expected_len != 0 && len != p.expected_len {
|
||||
eprintln!(
|
||||
"[lesavka-uvc] DATA len mismatch: expected={} got={}",
|
||||
p.expected_len, len
|
||||
);
|
||||
}
|
||||
let slice = &data.data[..len.min(data.data.len())];
|
||||
if debug && slice.len() >= STREAM_CTRL_SIZE_11 {
|
||||
let interval = read_le32(slice, 4);
|
||||
@ -524,9 +580,6 @@ fn handle_data(
|
||||
payload
|
||||
);
|
||||
}
|
||||
if std::env::var("LESAVKA_UVC_ACK_AFTER_DATA").is_ok() {
|
||||
let _ = send_response(fd, uvc_send_response, &[]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -711,6 +764,10 @@ fn env_u8(name: &str) -> Option<u8> {
|
||||
env::var(name).ok().and_then(|v| v.parse::<u8>().ok())
|
||||
}
|
||||
|
||||
fn env_u32_opt(name: &str) -> Option<u32> {
|
||||
env::var(name).ok().and_then(|v| v.parse::<u32>().ok())
|
||||
}
|
||||
|
||||
fn adjust_length(mut bytes: Vec<u8>, w_length: u16) -> Vec<u8> {
|
||||
let want = (w_length as usize).min(UVC_DATA_SIZE);
|
||||
if bytes.len() > want {
|
||||
@ -739,6 +796,65 @@ fn read_le32(src: &[u8], offset: usize) -> u32 {
|
||||
u32::from_le_bytes([src[offset], src[offset + 1], src[offset + 2], src[offset + 3]])
|
||||
}
|
||||
|
||||
fn compute_payload_cap(bulk: bool) -> Option<PayloadCap> {
|
||||
if let Some(limit) = env_u32_opt("LESAVKA_UVC_MAXPAYLOAD_LIMIT") {
|
||||
return Some(PayloadCap {
|
||||
limit,
|
||||
pct: 100,
|
||||
source: "env",
|
||||
periodic_dw: None,
|
||||
non_periodic_dw: None,
|
||||
});
|
||||
}
|
||||
|
||||
let periodic_dw = read_fifo_min("/sys/module/dwc2/parameters/g_tx_fifo_size");
|
||||
let non_periodic_dw = read_fifo_min("/sys/module/dwc2/parameters/g_np_tx_fifo_size");
|
||||
|
||||
let (fifo_dw, source) = if bulk {
|
||||
if let Some(np) = non_periodic_dw {
|
||||
(np, "dwc2.g_np_tx_fifo_size")
|
||||
} else if let Some(p) = periodic_dw {
|
||||
(p, "dwc2.g_tx_fifo_size")
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
} else if let Some(p) = periodic_dw {
|
||||
(p, "dwc2.g_tx_fifo_size")
|
||||
} else if let Some(np) = non_periodic_dw {
|
||||
(np, "dwc2.g_np_tx_fifo_size")
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let mut pct = env_u32("LESAVKA_UVC_LIMIT_PCT", 95);
|
||||
if pct == 0 {
|
||||
pct = 1;
|
||||
} else if pct > 100 {
|
||||
pct = 100;
|
||||
}
|
||||
let fifo_bytes = fifo_dw.saturating_mul(4);
|
||||
let limit = fifo_bytes.saturating_mul(pct) / 100;
|
||||
if limit == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(PayloadCap {
|
||||
limit,
|
||||
pct,
|
||||
source,
|
||||
periodic_dw,
|
||||
non_periodic_dw,
|
||||
})
|
||||
}
|
||||
|
||||
fn read_fifo_min(path: &str) -> Option<u32> {
|
||||
let raw = std::fs::read_to_string(path).ok()?;
|
||||
raw.split(|c: char| c == ',' || c.is_whitespace())
|
||||
.filter_map(|v| v.trim().parse::<u32>().ok())
|
||||
.filter(|v| *v > 0)
|
||||
.min()
|
||||
}
|
||||
|
||||
const IOC_NRBITS: u8 = 8;
|
||||
const IOC_TYPEBITS: u8 = 8;
|
||||
const IOC_SIZEBITS: u8 = 14;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user