use enum_dispatch::enum_dispatch;
use errno::Errno;
use nix::errno::errno;
use nix::{ioctl_read_buf, ioctl_readwrite_buf};
use rusb::{DeviceHandle, Direction, Recipient, RequestType};
use std::fs::File;
use std::os::unix::io::AsRawFd;
use std::time::Duration;
use glob::glob_with;
use glob::MatchOptions;
use std::str;
#[enum_dispatch(CameraHandleType)]
pub trait UvcUsbIo {
fn info(&self) -> Result<(), Errno>;
fn io(&self, unit: u8, selector: u8, query: u8, data: &mut [u8]) -> Result<(), Errno>;
}
#[derive(Debug)]
pub struct UvcCameraHandle {
handle: std::fs::File,
}
impl UvcUsbIo for UvcCameraHandle {
fn info(&self) -> Result<(), Errno> {
match v4l2_capability::new(&self.handle) {
Ok(video_info) => {
println!(
"Card: {}\nBus : {}",
str::from_utf8(&video_info.card).unwrap(),
str::from_utf8(&video_info.bus_info).unwrap()
);
Ok(())
}
_ => {
println!("Failed");
Err(errno::Errno(errno()))
}
}
}
fn io(&self, unit: u8, selector: u8, query: u8, data: &mut [u8]) -> Result<(), Errno> {
let dev = &self.handle;
let query = uvc_xu_control_query {
unit,
selector,
query,
size: data.len() as u16,
data: data.as_mut_ptr(),
};
unsafe {
match uvcioc_ctrl_query(dev.as_raw_fd(), &mut [query]) {
Ok(_) => Ok(()),
_ => Err(errno::Errno(errno())),
}
}
}
}
#[derive(Debug)]
pub struct UsbCameraHandle {
handle: DeviceHandle<rusb::GlobalContext>,
}
impl UvcUsbIo for UsbCameraHandle {
fn info(&self) -> Result<(), Errno> {
let camera = &self.handle;
let device_desc = camera.device().device_descriptor().unwrap();
let product = match camera.read_product_string_ascii(&device_desc) {
Ok(text) => text,
Err(_) => "unknown".to_string(),
};
let manufacturer = match camera.read_manufacturer_string_ascii(&device_desc) {
Ok(text) => text,
Err(_) => "unknown".to_string(),
};
println!(
"Opened device {:04x}:{:04x} {:}, {:}",
device_desc.vendor_id(),
device_desc.product_id(),
product,
manufacturer
);
Ok(())
}
fn io(&self, unit: u8, selector: u8, query: u8, data: &mut [u8]) -> Result<(), Errno> {
let camera = &self.handle;
let unit: u16 = (unit as u16) << 8;
let selector: u16 = (selector as u16) << 8;
if query < 128 {
let config_request_type =
rusb::request_type(Direction::Out, RequestType::Class, Recipient::Interface);
let size = match camera.write_control(
config_request_type,
query,
unit,
selector,
data,
Duration::from_millis(1000),
) {
Ok(size) => size,
Err(error) => panic!("Can't set config: {:?}", error),
};
println!("Size {}, data {:?}", size, data);
} else {
let config_request_type =
rusb::request_type(Direction::In, RequestType::Class, Recipient::Interface);
match camera.read_control(
config_request_type,
query,
unit,
selector,
data,
Duration::from_millis(1000),
) {
Ok(size) => size,
Err(error) => panic!("Can't set config: {:?}", error),
};
}
Ok(())
}
}
#[derive(Debug)]
#[enum_dispatch]
pub enum CameraHandleType {
UvcCameraHandle(UvcCameraHandle),
UsbCameraHandle(UsbCameraHandle),
}
impl CameraHandle {
pub fn info(&self) -> Result<(), Errno> {
match &self.camera_handle {
CameraHandleType::UvcCameraHandle(handle) => handle.info(),
CameraHandleType::UsbCameraHandle(handle) => handle.info(),
}
}
pub fn io(&self, unit: u8, selector: u8, query: u8, data: &mut [u8]) -> Result<(), Errno> {
match &self.camera_handle {
CameraHandleType::UvcCameraHandle(handle) => handle.io(unit, selector, query, data),
CameraHandleType::UsbCameraHandle(handle) => handle.io(unit, selector, query, data),
}
}
}
#[derive(Debug)]
pub struct CameraHandle {
pub camera_handle: CameraHandleType,
}
pub fn open_camera(hint: &str) -> Result<CameraHandle, errno::Errno> {
match uvc_open_camera(hint) {
Ok(file) => {
Ok(CameraHandle {
camera_handle: CameraHandleType::UvcCameraHandle(UvcCameraHandle { handle: file }),
})
}
Err(_) => match usb_open_camera(hint) {
Ok(dev) => {
Ok(CameraHandle {
camera_handle: CameraHandleType::UsbCameraHandle(UsbCameraHandle {
handle: dev,
}),
})
}
Err(error) => panic!("Can't open {} {:?}", hint, error),
},
}
}
fn uvc_open_camera(hint: &str) -> Result<std::fs::File, errno::Errno> {
match File::open(hint) {
Ok(file) => return Ok(file),
Err(_) => 0, };
match File::open("/dev/".to_owned() + hint) {
Ok(file) => return Ok(file),
Err(_) => 0, };
let options = MatchOptions {
case_sensitive: true,
require_literal_separator: true,
require_literal_leading_dot: true,
};
for entry in glob_with("/dev/video*", options).unwrap() {
if let Ok(path) = entry {
if let Ok(device) = File::open(&path) {
if let Ok(video_info) = v4l2_capability::new(&device) {
if (str::from_utf8(&video_info.card)
.unwrap()
.contains(hint)
|| str::from_utf8(&video_info.bus_info)
.unwrap()
.contains(hint))
&& (video_info.device_caps & 0x800000 == 0)
{
return Ok(device);
}
}
}
}
}
Err(errno::Errno(errno()))}
#[repr(C)]
#[derive(Copy, Clone, Default, Debug)]
pub struct v4l2_capability {
driver: [u8; 16],
card: [u8; 32],
bus_info: [u8; 32],
version: u32,
capabilities: u32,
device_caps: u32,
reserved: [u32; 3],
}
impl v4l2_capability {
fn new(dev: &std::fs::File) -> Result<Self, errno::Errno> {
let mut query = [v4l2_capability {
..Default::default()
}];
unsafe {
match ioctl_videoc_querycap(dev.as_raw_fd(), &mut query) {
Ok(_) => {
Ok(query[0])
}
_ => {
Err(errno::Errno(errno()))
}
}
}
}
}
const VIDIOC_QUERYCAP_MAGIC: u8 = b'V';
const VIDIOC_QUERYCAP_QUERY_MESSAGE: u8 = 0x00;
ioctl_read_buf!(
ioctl_videoc_querycap,
VIDIOC_QUERYCAP_MAGIC,
VIDIOC_QUERYCAP_QUERY_MESSAGE,
v4l2_capability
);
#[repr(C)]
pub struct uvc_xu_control_query {
unit: u8,
selector: u8,
query: u8,
size: u16,
data: *mut u8,
}
const UVCIOC_CTRL_MAGIC: u8 = b'u'; const UVCIOC_CTRL_QUERY_MESSAGE: u8 = 0x21; ioctl_readwrite_buf!(
uvcioc_ctrl_query,
UVCIOC_CTRL_MAGIC,
UVCIOC_CTRL_QUERY_MESSAGE,
uvc_xu_control_query
);
ioctl_read_buf!(
uvcioc_ctrl_query_read,
UVCIOC_CTRL_MAGIC,
UVCIOC_CTRL_QUERY_MESSAGE,
uvc_xu_control_query
);
#[allow(dead_code)]
const UVC_RC_UNDEFINED: u8 = 0x00;
#[allow(dead_code)]
pub const UVC_SET_CUR: u8 = 0x01;
#[allow(dead_code)]
pub const UVC_GET_CUR: u8 = 0x81;
#[allow(dead_code)]
const UVC_GET_MIN: u8 = 0x82;
#[allow(dead_code)]
const UVC_GET_MAX: u8 = 0x83;
#[allow(dead_code)]
const UVC_GET_RES: u8 = 0x84;
#[allow(dead_code)]
pub const UVC_GET_LEN: u8 = 0x85;
#[allow(dead_code)]
const UVC_GET_INFO: u8 = 0x86;
#[allow(dead_code)]
const UVC_GET_DEF: u8 = 0x87;
pub fn usb_open_camera(hint: &str) -> Result<DeviceHandle<rusb::GlobalContext>, rusb::Error> {
for device in rusb::devices().unwrap().iter() {
let device_desc = device.device_descriptor().unwrap();
match device.open() {
Ok(mut camera) => {
let product = match camera.read_product_string_ascii(&device_desc) {
Ok(text) => text,
Err(_) => "unknown".to_string(),
};
let manufacturer = match camera.read_manufacturer_string_ascii(&device_desc) {
Ok(text) => text,
Err(_) => "unknown".to_string(),
};
if format!(
"{:04x}:{:04x}",
device_desc.vendor_id(),
device_desc.product_id()
)
.eq(hint)
|| product.contains(hint)
|| manufacturer.contains(hint)
{
camera.set_auto_detach_kernel_driver(true)?;
camera.claim_interface(0)?;
return Ok(camera);
}
}
_ => (),
}
}
Err(rusb::Error::NoDevice)
}