server: read UVC interface numbers

This commit is contained in:
Brad Stein 2026-01-06 13:22:06 -03:00
parent d9d2bd6c73
commit 7ea2f83002

View File

@ -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<u8> {
std::fs::read_to_string(path)
.ok()
.and_then(|v| v.trim().parse::<u8>().ok())
}
fn open_with_retry(path: &str) -> Result<std::fs::File> {
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<PendingRequest>,
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<PendingRequest>,
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<Vec<u8>> {
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<u8> {
env::var(name).ok().and_then(|v| v.parse::<u8>().ok())
}
fn write_le16(dst: &mut [u8], val: u16) {
let bytes = val.to_le_bytes();
dst[0] = bytes[0];