From 7ea2f8300208e2015281e597e34a82338a18ee5b Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Tue, 6 Jan 2026 13:22:06 -0300 Subject: [PATCH] server: read UVC interface numbers --- server/src/bin/lesavka-uvc.rs | 51 +++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/server/src/bin/lesavka-uvc.rs b/server/src/bin/lesavka-uvc.rs index 1b706b6..9da8326 100644 --- a/server/src/bin/lesavka-uvc.rs +++ b/server/src/bin/lesavka-uvc.rs @@ -101,9 +101,20 @@ struct PendingRequest { selector: u8, } +#[derive(Clone, Copy)] +struct UvcInterfaces { + control: u8, + streaming: u8, +} + fn main() -> Result<()> { let (dev, cfg) = parse_args()?; + let interfaces = load_interfaces(); eprintln!("[lesavka-uvc] starting (dev={dev})"); + eprintln!( + "[lesavka-uvc] interfaces control={} streaming={}", + interfaces.control, interfaces.streaming + ); let debug = env::var("LESAVKA_UVC_DEBUG").is_ok(); let mut setup_seen: u64 = 0; @@ -184,6 +195,7 @@ fn main() -> Result<()> { uvc_send_response, &mut state, &mut pending, + interfaces, req, debug, ); @@ -197,7 +209,7 @@ fn main() -> Result<()> { data.length ); } - handle_data(&mut state, &mut pending, data, debug); + handle_data(&mut state, &mut pending, interfaces, data, debug); } _ => { if debug { @@ -268,6 +280,22 @@ impl UvcState { } } +fn load_interfaces() -> UvcInterfaces { + let control = env_u8("LESAVKA_UVC_CTRL_INTF") + .or_else(|| read_interface("/sys/kernel/config/usb_gadget/lesavka/functions/uvc.usb0/control/bInterfaceNumber")) + .unwrap_or(UVC_STRING_CONTROL_IDX); + let streaming = env_u8("LESAVKA_UVC_STREAM_INTF") + .or_else(|| read_interface("/sys/kernel/config/usb_gadget/lesavka/functions/uvc.usb0/streaming/bInterfaceNumber")) + .unwrap_or(UVC_STRING_STREAMING_IDX); + UvcInterfaces { control, streaming } +} + +fn read_interface(path: &str) -> Option { + std::fs::read_to_string(path) + .ok() + .and_then(|v| v.trim().parse::().ok()) +} + fn open_with_retry(path: &str) -> Result { for attempt in 1..=200 { let mut opts = OpenOptions::new(); @@ -310,6 +338,7 @@ fn handle_setup( uvc_send_response: libc::c_ulong, state: &mut UvcState, pending: &mut Option, + interfaces: UvcInterfaces, req: UsbCtrlRequest, debug: bool, ) { @@ -318,6 +347,10 @@ fn handle_setup( let is_in = (req.b_request_type & USB_DIR_IN) != 0; if !is_in && req.b_request == UVC_SET_CUR { + if interface != interfaces.streaming { + 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)]; @@ -336,7 +369,7 @@ fn handle_setup( return; } - let payload = build_in_response(state, interface, selector, req.b_request); + let payload = build_in_response(state, interfaces, interface, selector, req.b_request); match payload { Some(bytes) => { let _ = send_response(fd, uvc_send_response, &bytes); @@ -350,6 +383,7 @@ fn handle_setup( fn handle_data( state: &mut UvcState, pending: &mut Option, + interfaces: UvcInterfaces, data: UvcRequestData, debug: bool, ) { @@ -375,7 +409,7 @@ fn handle_data( ); } - if p.interface == UVC_STRING_STREAMING_IDX + if p.interface == interfaces.streaming && matches!(p.selector, UVC_VS_PROBE_CONTROL | UVC_VS_COMMIT_CONTROL) { let sanitized = sanitize_streaming_control(slice, state); @@ -407,13 +441,16 @@ fn handle_data( fn build_in_response( state: &UvcState, + interfaces: UvcInterfaces, interface: u8, selector: u8, request: u8, ) -> Option> { match interface { - UVC_STRING_STREAMING_IDX => build_streaming_response(state, selector, request), - UVC_STRING_CONTROL_IDX => build_control_response(selector, request), + _ if interface == interfaces.streaming => { + build_streaming_response(state, selector, request) + } + _ if interface == interfaces.control => build_control_response(selector, request), _ => None, } } @@ -561,6 +598,10 @@ fn env_u32(name: &str, default: u32) -> u32 { .unwrap_or(default) } +fn env_u8(name: &str) -> Option { + env::var(name).ok().and_then(|v| v.parse::().ok()) +} + fn write_le16(dst: &mut [u8], val: u16) { let bytes = val.to_le_bytes(); dst[0] = bytes[0];