server: fix UVC control length

This commit is contained in:
Brad Stein 2026-01-06 14:28:00 -03:00
parent 7ea2f83002
commit f043634ce2

View File

@ -8,7 +8,9 @@ use std::os::unix::io::AsRawFd;
use std::thread;
use std::time::Duration;
const STREAM_CTRL_SIZE: usize = 34;
const STREAM_CTRL_SIZE_11: usize = 26;
const STREAM_CTRL_SIZE_15: usize = 34;
const STREAM_CTRL_SIZE_MAX: usize = STREAM_CTRL_SIZE_15;
const UVC_DATA_SIZE: usize = 60;
const V4L2_EVENT_PRIVATE_START: u32 = 0x0800_0000;
@ -90,9 +92,10 @@ struct UvcConfig {
struct UvcState {
cfg: UvcConfig,
default: [u8; STREAM_CTRL_SIZE],
probe: [u8; STREAM_CTRL_SIZE],
commit: [u8; STREAM_CTRL_SIZE],
ctrl_len: usize,
default: [u8; STREAM_CTRL_SIZE_MAX],
probe: [u8; STREAM_CTRL_SIZE_MAX],
commit: [u8; STREAM_CTRL_SIZE_MAX],
}
#[derive(Clone, Copy)]
@ -270,9 +273,11 @@ impl UvcConfig {
impl UvcState {
fn new(cfg: UvcConfig) -> Self {
let default = build_streaming_control(&cfg);
let ctrl_len = stream_ctrl_len();
let default = build_streaming_control(&cfg, ctrl_len);
Self {
cfg,
ctrl_len,
default,
probe: default,
commit: default,
@ -342,7 +347,15 @@ fn handle_setup(
req: UsbCtrlRequest,
debug: bool,
) {
let interface = (req.w_index & 0xff) as u8;
let interface_lo = (req.w_index & 0xff) as u8;
let interface_hi = (req.w_index >> 8) as u8;
let interface = if interface_lo == interfaces.streaming || interface_lo == interfaces.control {
interface_lo
} else if interface_hi == interfaces.streaming || interface_hi == interfaces.control {
interface_hi
} else {
interface_lo
};
let selector = (req.w_value >> 8) as u8;
let is_in = (req.b_request_type & USB_DIR_IN) != 0;
@ -369,7 +382,8 @@ fn handle_setup(
return;
}
let payload = build_in_response(state, interfaces, interface, selector, req.b_request);
let payload =
build_in_response(state, interfaces, interface, selector, req.b_request, req.w_length);
match payload {
Some(bytes) => {
let _ = send_response(fd, uvc_send_response, &bytes);
@ -397,7 +411,7 @@ fn handle_data(
let len = data.length as usize;
let slice = &data.data[..len.min(data.data.len())];
if debug && slice.len() >= STREAM_CTRL_SIZE {
if debug && slice.len() >= STREAM_CTRL_SIZE_11 {
let interval = read_le32(slice, 4);
let payload = read_le32(slice, 22);
eprintln!(
@ -445,14 +459,17 @@ fn build_in_response(
interface: u8,
selector: u8,
request: u8,
w_length: u16,
) -> Option<Vec<u8>> {
match interface {
let payload = match interface {
_ if interface == interfaces.streaming => {
build_streaming_response(state, selector, request)
}
_ if interface == interfaces.control => build_control_response(selector, request),
_ => None,
}
}?;
Some(adjust_length(payload, w_length))
}
fn build_streaming_response(
@ -468,9 +485,11 @@ fn build_streaming_response(
match request {
UVC_GET_INFO => Some(vec![0x03]),
UVC_GET_LEN => Some((STREAM_CTRL_SIZE as u16).to_le_bytes().to_vec()),
UVC_GET_CUR => Some(current.to_vec()),
UVC_GET_MIN | UVC_GET_MAX | UVC_GET_DEF | UVC_GET_RES => Some(state.default.to_vec()),
UVC_GET_LEN => Some((state.ctrl_len as u16).to_le_bytes().to_vec()),
UVC_GET_CUR => Some(current[..state.ctrl_len].to_vec()),
UVC_GET_MIN | UVC_GET_MAX | UVC_GET_DEF | UVC_GET_RES => {
Some(state.default[..state.ctrl_len].to_vec())
}
_ => None,
}
}
@ -490,9 +509,12 @@ fn build_control_response(selector: u8, request: u8) -> Option<Vec<u8>> {
}
}
fn sanitize_streaming_control(data: &[u8], state: &UvcState) -> [u8; STREAM_CTRL_SIZE] {
fn sanitize_streaming_control(
data: &[u8],
state: &UvcState,
) -> [u8; STREAM_CTRL_SIZE_MAX] {
let mut out = state.default;
if data.len() >= STREAM_CTRL_SIZE {
if data.len() >= STREAM_CTRL_SIZE_11 {
let format_index = data[2];
let frame_index = data[3];
let interval = read_le32(data, 4);
@ -546,8 +568,8 @@ fn send_stall(fd: i32, req: libc::c_ulong) -> Result<()> {
Ok(())
}
fn build_streaming_control(cfg: &UvcConfig) -> [u8; STREAM_CTRL_SIZE] {
let mut buf = [0u8; STREAM_CTRL_SIZE];
fn build_streaming_control(cfg: &UvcConfig, ctrl_len: usize) -> [u8; STREAM_CTRL_SIZE_MAX] {
let mut buf = [0u8; STREAM_CTRL_SIZE_MAX];
let frame_size = cfg.width * cfg.height * 2;
write_le16(&mut buf[0..2], 1); // bmHint: dwFrameInterval
@ -561,11 +583,13 @@ fn build_streaming_control(cfg: &UvcConfig) -> [u8; STREAM_CTRL_SIZE] {
write_le16(&mut buf[16..18], 0);
write_le32(&mut buf[18..22], frame_size);
write_le32(&mut buf[22..26], cfg.max_packet);
write_le32(&mut buf[26..30], 48_000_000);
buf[30] = 0;
buf[31] = 0;
buf[32] = 0;
buf[33] = 0;
if ctrl_len >= STREAM_CTRL_SIZE_15 {
write_le32(&mut buf[26..30], 48_000_000);
buf[30] = 0;
buf[31] = 0;
buf[32] = 0;
buf[33] = 0;
}
buf
}
@ -591,6 +615,14 @@ fn parse_request_data(data: [u8; 64]) -> UvcRequestData {
UvcRequestData { length, data: out }
}
fn stream_ctrl_len() -> usize {
let value = env_u32("LESAVKA_UVC_CTRL_LEN", STREAM_CTRL_SIZE_11 as u32) as usize;
match value {
STREAM_CTRL_SIZE_11 | STREAM_CTRL_SIZE_15 => value,
_ => STREAM_CTRL_SIZE_11,
}
}
fn env_u32(name: &str, default: u32) -> u32 {
env::var(name)
.ok()
@ -602,6 +634,16 @@ fn env_u8(name: &str) -> Option<u8> {
env::var(name).ok().and_then(|v| v.parse::<u8>().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 {
bytes.truncate(want);
} else if bytes.len() < want {
bytes.resize(want, 0);
}
bytes
}
fn write_le16(dst: &mut [u8], val: u16) {
let bytes = val.to_le_bytes();
dst[0] = bytes[0];