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_DISABLE_IRQ=${LESAVKA_UVC_DISABLE_IRQ:-}
|
||||||
UVC_BULK=${LESAVKA_UVC_BULK:-}
|
UVC_BULK=${LESAVKA_UVC_BULK:-}
|
||||||
UVC_CODEC=${LESAVKA_UVC_CODEC:-yuyv}
|
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
|
if [[ -n ${LESAVKA_UVC_MJPEG:-} ]]; then
|
||||||
UVC_CODEC=mjpeg
|
UVC_CODEC=mjpeg
|
||||||
fi
|
fi
|
||||||
|
|||||||
@ -91,6 +91,14 @@ struct UvcConfig {
|
|||||||
frame_size: u32,
|
frame_size: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct PayloadCap {
|
||||||
|
limit: u32,
|
||||||
|
pct: u32,
|
||||||
|
source: &'static str,
|
||||||
|
periodic_dw: Option<u32>,
|
||||||
|
non_periodic_dw: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
struct UvcState {
|
struct UvcState {
|
||||||
cfg: UvcConfig,
|
cfg: UvcConfig,
|
||||||
ctrl_len: usize,
|
ctrl_len: usize,
|
||||||
@ -103,6 +111,7 @@ struct UvcState {
|
|||||||
struct PendingRequest {
|
struct PendingRequest {
|
||||||
interface: u8,
|
interface: u8,
|
||||||
selector: u8,
|
selector: u8,
|
||||||
|
expected_len: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
@ -261,6 +270,32 @@ impl UvcConfig {
|
|||||||
let interval = env_u32("LESAVKA_UVC_INTERVAL", 0);
|
let interval = env_u32("LESAVKA_UVC_INTERVAL", 0);
|
||||||
let mut max_packet = env_u32("LESAVKA_UVC_MAXPACKET", 1024);
|
let mut max_packet = env_u32("LESAVKA_UVC_MAXPACKET", 1024);
|
||||||
let frame_size = env_u32("LESAVKA_UVC_FRAME_SIZE", width * height * 2);
|
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() {
|
if env::var("LESAVKA_UVC_BULK").is_ok() {
|
||||||
max_packet = max_packet.min(512);
|
max_packet = max_packet.min(512);
|
||||||
} else {
|
} else {
|
||||||
@ -383,9 +418,21 @@ fn handle_setup(
|
|||||||
let _ = send_stall(fd, uvc_send_response);
|
let _ = send_stall(fd, uvc_send_response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
*pending = Some(PendingRequest { interface, selector });
|
|
||||||
let len = req.w_length as usize;
|
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);
|
let _ = send_response(fd, uvc_send_response, &payload);
|
||||||
if debug {
|
if debug {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
@ -477,6 +524,9 @@ fn handle_data(
|
|||||||
debug: bool,
|
debug: bool,
|
||||||
) {
|
) {
|
||||||
let Some(p) = pending.take() else {
|
let Some(p) = pending.take() else {
|
||||||
|
if debug {
|
||||||
|
eprintln!("[lesavka-uvc] DATA with no pending request; ignoring");
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -485,6 +535,12 @@ fn handle_data(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let len = data.length as usize;
|
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())];
|
let slice = &data.data[..len.min(data.data.len())];
|
||||||
if debug && slice.len() >= STREAM_CTRL_SIZE_11 {
|
if debug && slice.len() >= STREAM_CTRL_SIZE_11 {
|
||||||
let interval = read_le32(slice, 4);
|
let interval = read_le32(slice, 4);
|
||||||
@ -524,9 +580,6 @@ fn handle_data(
|
|||||||
payload
|
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())
|
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> {
|
fn adjust_length(mut bytes: Vec<u8>, w_length: u16) -> Vec<u8> {
|
||||||
let want = (w_length as usize).min(UVC_DATA_SIZE);
|
let want = (w_length as usize).min(UVC_DATA_SIZE);
|
||||||
if bytes.len() > want {
|
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]])
|
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_NRBITS: u8 = 8;
|
||||||
const IOC_TYPEBITS: u8 = 8;
|
const IOC_TYPEBITS: u8 = 8;
|
||||||
const IOC_SIZEBITS: u8 = 14;
|
const IOC_SIZEBITS: u8 = 14;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user