server: read UVC interface numbers
This commit is contained in:
parent
d9d2bd6c73
commit
7ea2f83002
@ -101,9 +101,20 @@ struct PendingRequest {
|
|||||||
selector: u8,
|
selector: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct UvcInterfaces {
|
||||||
|
control: u8,
|
||||||
|
streaming: u8,
|
||||||
|
}
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let (dev, cfg) = parse_args()?;
|
let (dev, cfg) = parse_args()?;
|
||||||
|
let interfaces = load_interfaces();
|
||||||
eprintln!("[lesavka-uvc] starting (dev={dev})");
|
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 debug = env::var("LESAVKA_UVC_DEBUG").is_ok();
|
||||||
let mut setup_seen: u64 = 0;
|
let mut setup_seen: u64 = 0;
|
||||||
@ -184,6 +195,7 @@ fn main() -> Result<()> {
|
|||||||
uvc_send_response,
|
uvc_send_response,
|
||||||
&mut state,
|
&mut state,
|
||||||
&mut pending,
|
&mut pending,
|
||||||
|
interfaces,
|
||||||
req,
|
req,
|
||||||
debug,
|
debug,
|
||||||
);
|
);
|
||||||
@ -197,7 +209,7 @@ fn main() -> Result<()> {
|
|||||||
data.length
|
data.length
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
handle_data(&mut state, &mut pending, data, debug);
|
handle_data(&mut state, &mut pending, interfaces, data, debug);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
if 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> {
|
fn open_with_retry(path: &str) -> Result<std::fs::File> {
|
||||||
for attempt in 1..=200 {
|
for attempt in 1..=200 {
|
||||||
let mut opts = OpenOptions::new();
|
let mut opts = OpenOptions::new();
|
||||||
@ -310,6 +338,7 @@ fn handle_setup(
|
|||||||
uvc_send_response: libc::c_ulong,
|
uvc_send_response: libc::c_ulong,
|
||||||
state: &mut UvcState,
|
state: &mut UvcState,
|
||||||
pending: &mut Option<PendingRequest>,
|
pending: &mut Option<PendingRequest>,
|
||||||
|
interfaces: UvcInterfaces,
|
||||||
req: UsbCtrlRequest,
|
req: UsbCtrlRequest,
|
||||||
debug: bool,
|
debug: bool,
|
||||||
) {
|
) {
|
||||||
@ -318,6 +347,10 @@ fn handle_setup(
|
|||||||
let is_in = (req.b_request_type & USB_DIR_IN) != 0;
|
let is_in = (req.b_request_type & USB_DIR_IN) != 0;
|
||||||
|
|
||||||
if !is_in && req.b_request == UVC_SET_CUR {
|
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 });
|
*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)];
|
let payload = vec![0u8; len.min(UVC_DATA_SIZE)];
|
||||||
@ -336,7 +369,7 @@ fn handle_setup(
|
|||||||
return;
|
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 {
|
match payload {
|
||||||
Some(bytes) => {
|
Some(bytes) => {
|
||||||
let _ = send_response(fd, uvc_send_response, &bytes);
|
let _ = send_response(fd, uvc_send_response, &bytes);
|
||||||
@ -350,6 +383,7 @@ fn handle_setup(
|
|||||||
fn handle_data(
|
fn handle_data(
|
||||||
state: &mut UvcState,
|
state: &mut UvcState,
|
||||||
pending: &mut Option<PendingRequest>,
|
pending: &mut Option<PendingRequest>,
|
||||||
|
interfaces: UvcInterfaces,
|
||||||
data: UvcRequestData,
|
data: UvcRequestData,
|
||||||
debug: bool,
|
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)
|
&& matches!(p.selector, UVC_VS_PROBE_CONTROL | UVC_VS_COMMIT_CONTROL)
|
||||||
{
|
{
|
||||||
let sanitized = sanitize_streaming_control(slice, state);
|
let sanitized = sanitize_streaming_control(slice, state);
|
||||||
@ -407,13 +441,16 @@ fn handle_data(
|
|||||||
|
|
||||||
fn build_in_response(
|
fn build_in_response(
|
||||||
state: &UvcState,
|
state: &UvcState,
|
||||||
|
interfaces: UvcInterfaces,
|
||||||
interface: u8,
|
interface: u8,
|
||||||
selector: u8,
|
selector: u8,
|
||||||
request: u8,
|
request: u8,
|
||||||
) -> Option<Vec<u8>> {
|
) -> Option<Vec<u8>> {
|
||||||
match interface {
|
match interface {
|
||||||
UVC_STRING_STREAMING_IDX => build_streaming_response(state, selector, request),
|
_ if interface == interfaces.streaming => {
|
||||||
UVC_STRING_CONTROL_IDX => build_control_response(selector, request),
|
build_streaming_response(state, selector, request)
|
||||||
|
}
|
||||||
|
_ if interface == interfaces.control => build_control_response(selector, request),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -561,6 +598,10 @@ fn env_u32(name: &str, default: u32) -> u32 {
|
|||||||
.unwrap_or(default)
|
.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) {
|
fn write_le16(dst: &mut [u8], val: u16) {
|
||||||
let bytes = val.to_le_bytes();
|
let bytes = val.to_le_bytes();
|
||||||
dst[0] = bytes[0];
|
dst[0] = bytes[0];
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user