server: fix UVC control length
This commit is contained in:
parent
7ea2f83002
commit
f043634ce2
@ -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];
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user