1
0
mirror of https://github.com/marek-g/it9910hd_fusefs.git synced 2026-05-05 18:01:19 +09:00

New project (based on PoC).

This commit is contained in:
Marek G
2019-08-16 17:28:19 +02:00
commit 25df808008
8 changed files with 549 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
/target
**/*.rs.bk
output.ts

16
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,16 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "it9910hd_fusefs",
"request": "launch",
"type": "gdb",
"target": "${workspaceRoot}/target/debug/it9910hd_fusefs",
"cwd": "${workspaceRoot}",
"env": {
"RUST_BACKTRACE": "1"
},
"preLaunchTask": "build_it9910hd_fusefs"
},
]
}

12
.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,12 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"taskName": "build_it9910hd_fusefs",
"type": "shell",
"command": "cargo build"
},
]
}

58
Cargo.lock generated Normal file
View File

@@ -0,0 +1,58 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "bit-set"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bit-vec 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bit-vec"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "it9910hd_fusefs"
version = "0.1.0"
dependencies = [
"libusb 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "libc"
version = "0.2.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libusb"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bit-set 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"libusb-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "libusb-sys"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "pkg-config"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum bit-set 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e6e1e6fb1c9e3d6fcdec57216a74eaa03e41f52a22f13a16438251d8e88b89da"
"checksum bit-vec 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a4523a10839ffae575fb08aa3423026c8cb4687eef43952afb956229d4f246f7"
"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba"
"checksum libusb 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5f990ddd929cbe53de4ecd6cf26e1f4e0c5b9796e4c629d9046570b03738aa53"
"checksum libusb-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4c53b6582563d64ad3e692f54ef95239c3ea8069e82c9eb70ca948869a7ad767"
"checksum pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c1d2cfa5a714db3b5f24f0915e74fcdf91d09d496ba61329705dda7774d2af"

10
Cargo.toml Normal file
View File

@@ -0,0 +1,10 @@
[package]
name = "it9910hd_fusefs"
version = "0.1.0"
authors = ["Marek G <marek@nomail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
libusb = "0.3"

15
README.md Normal file
View File

@@ -0,0 +1,15 @@
# IT9910HD FUSE FS
FUSE File System driver for IT9910HD HDMI MPEG4 (H.264) capture device.
## Setup USB permissions
1. Create or open `/etc/udev/rules.d/50-myusb.rules` file.
2. Add new line:
```
SUBSYSTEMS=="usb", ATTRS{idVendor}=="048d", ATTRS{idProduct}=="9910", GROUP="users", MODE="0666"
```
3. Reload udev rules:
```
sudo udevadm control --reload
```

426
src/it9910hd_driver.rs Normal file
View File

@@ -0,0 +1,426 @@
use std::io::Write;
use std::slice;
use std::time::Duration;
#[derive(Debug)]
struct Endpoint {
config: u8,
iface: u8,
setting: u8,
address: u8,
}
pub fn run() -> Result<(), String> {
let vid: u16 = 0x048D;
let pid: u16 = 0x9910;
let libusb_context = match libusb::Context::new() {
Ok(context) => context,
Err(e) => return Err(format!("could not initialize libusb: {}", e)),
};
let devices = match libusb_context.devices() {
Ok(d) => d,
Err(e) => return Err(format!("could not get list of devices: {}", e)),
};
let (device, _device_desc) = match devices
.iter()
.map(|d| {
let dd = d.device_descriptor();
(d, dd)
})
.find(|(_, dd)| {
if let Ok(dd) = dd {
dd.vendor_id() == vid && dd.product_id() == pid
} else {
false
}
}) {
Some((device, Ok(device_desc))) => (device, device_desc),
_ => {
return Err(format!(
"could not open device: VID: {:04x} PID: {:04x}",
vid, pid
))
}
};
let mut handle = match device.open() {
Ok(handle) => handle,
Err(e) => return Err(format!("could not open device: {}", e)),
};
// get endpoints
let endpoint_command_read: Endpoint;
let endpoint_command_write: Endpoint;
let endpoint_data_read: Endpoint;
let config_desc = device.config_descriptor(0).unwrap();
let interface = config_desc.interfaces().next().unwrap();
let interface_desc = interface.descriptors().next().unwrap();
let mut interface_descriptors = interface_desc.endpoint_descriptors();
endpoint_command_read = {
let endpoint_desc = interface_descriptors.next().unwrap();
Endpoint {
config: config_desc.number(),
iface: interface_desc.interface_number(),
setting: interface_desc.setting_number(),
address: endpoint_desc.address(),
}
};
endpoint_command_write = {
let endpoint_desc = interface_descriptors.next().unwrap();
Endpoint {
config: config_desc.number(),
iface: interface_desc.interface_number(),
setting: interface_desc.setting_number(),
address: endpoint_desc.address(),
}
};
endpoint_data_read = {
let endpoint_desc = interface_descriptors.next().unwrap();
Endpoint {
config: config_desc.number(),
iface: interface_desc.interface_number(),
setting: interface_desc.setting_number(),
address: endpoint_desc.address(),
}
};
let has_kernel_driver = match handle.kernel_driver_active(interface_desc.interface_number()) {
Ok(true) => {
handle
.detach_kernel_driver(interface_desc.interface_number())
.ok();
true
}
_ => false,
};
handle
.set_active_configuration(config_desc.number())
.unwrap();
handle
.claim_interface(interface_desc.interface_number())
.unwrap();
handle
.set_alternate_setting(
interface_desc.interface_number(),
interface_desc.setting_number(),
)
.unwrap();
stream_video(
&mut handle,
&endpoint_command_read,
&endpoint_command_write,
&endpoint_data_read,
)?;
if has_kernel_driver {
handle
.attach_kernel_driver(interface_desc.interface_number())
.ok();
}
Ok(())
}
fn stream_video(
handle: &mut libusb::DeviceHandle,
endpoint_command_read: &Endpoint,
endpoint_command_write: &Endpoint,
endpoint_data_read: &Endpoint,
) -> Result<(), String> {
let mut transfer_handle = TransferHandle {
device_handle: &handle,
counter: 0,
read_addr: endpoint_command_read.address,
write_addr: endpoint_command_write.address,
data_addr: endpoint_data_read.address,
};
//transfer_handle.debug_query_time(1)?;
//transfer_handle.set_pc_grabber(0)?;
//std::thread::sleep(std::time::Duration::from_millis(2000));
//transfer_handle.debug_query_time(1)?;
//transfer_handle.get_source()?;
transfer_handle.set_pc_grabber(1)?;
loop {
let res = transfer_handle.get_pc_grabber()?;
if res > 0 {
break;
}
std::thread::sleep(std::time::Duration::from_millis(500));
}
//transfer_handle.debug_query_time(1)?;
let device_model = transfer_handle.get_hw_grabber()?;
if device_model == 2 {
transfer_handle.set_source(2, 4)?;
}
for i in 0..35 {
transfer_handle.set_pc_grabber2(device_model, i, 1920, 1080, 10000, 25)?;
}
//transfer_handle.debug_query_time(1)?;
transfer_handle.set_state(2)?;
let mut file = std::fs::File::create("output.ts").unwrap();
for _j in 0..10 {
// read data always in 16 chunks before issuing other command
for _i in 0..16 {
let data = transfer_handle.read_data()?;
file.write(&data).unwrap();
}
}
transfer_handle.set_state(0)?;
transfer_handle.set_pc_grabber(0)?;
Ok(())
}
struct TransferHandle<'a> {
device_handle: &'a libusb::DeviceHandle<'a>,
counter: u32,
read_addr: u8,
write_addr: u8,
data_addr: u8,
}
impl<'a> TransferHandle<'a> {
pub fn debug_query_time(&mut self, time: i32) -> Result<i32, String> {
let mut buf = [0u8; 16 + 4];
write_le_i32(&mut buf[16..20], time);
let received = self.send_command(&mut buf, 0x9910F001, 1)?;
let result_time = read_le_i32(&received[16..20]);
println!("DebugQueryTime({}) -> {}", time, result_time);
Ok(result_time)
}
pub fn get_source(&mut self) -> Result<(i32, i32), String> {
let mut buf = [0u8; 16 + 4 * 2];
let received = self.send_command(&mut buf, 0x99100003, 1)?;
let audio_source = read_le_i32(&received[16..20]);
let video_source = read_le_i32(&received[20..24]);
println!("GetSource() -> ({}, {})", audio_source, video_source);
Ok((audio_source, video_source))
}
pub fn set_source(&mut self, audio_source: i32, video_source: i32) -> Result<(), String> {
let mut buf = [0u8; 16 + 4 * 2];
write_le_i32(&mut buf[16..20], audio_source);
write_le_i32(&mut buf[20..24], video_source);
let _received = self.send_command(&mut buf, 0x99100003, 2)?;
println!("SetSource() -> ({}, {})", audio_source, video_source);
Ok(())
}
pub fn set_pc_grabber(&mut self, start: i32) -> Result<(), String> {
let mut buf = [0u8; 16 + 4 * 3];
write_le_u32(&mut buf[16..20], 0x38384001);
write_le_i32(&mut buf[24..28], start);
let _received = self.send_command(&mut buf, 0x9910E001, 2)?;
println!("SetPCGrabber({})", start);
Ok(())
}
pub fn set_pc_grabber2(
&mut self,
device_model: i32,
counter: u32,
width: u32,
height: u32,
kbitrate: u32,
framerate: u32,
) -> Result<(), String> {
let mut buf = [0u8; 16 + 4 * 15];
write_le_u32(&mut buf[16..20], 0x38382008);
write_le_u32(&mut buf[24..28], if device_model == 2 { 4 } else { 5 });
write_le_u32(&mut buf[28..32], counter);
write_le_u32(&mut buf[32..36], 15);
write_le_u32(&mut buf[36..40], width);
write_le_u32(&mut buf[40..44], height);
write_le_u32(&mut buf[44..48], kbitrate);
write_le_u32(&mut buf[56..60], framerate);
let _received = self.send_command(&mut buf, 0x9910E001, 2)?;
println!("SetPCGrabber2({})", counter);
Ok(())
}
pub fn get_pc_grabber(&mut self) -> Result<i32, String> {
let mut buf = [0u8; 16 + 4 * 3];
write_le_u32(&mut buf[16..20], 0x38384001);
let received = self.send_command(&mut buf, 0x9910E001, 1)?;
let result = read_le_i32(&received[24..28]);
println!("GetPCGrabber() -> {}", result);
Ok(result)
}
pub fn get_hw_grabber(&mut self) -> Result<(i32), String> {
let mut buf = [0u8; 16 + 4 * 35 + 2];
write_le_u32(&mut buf[16..20], 8);
let received = self.send_command(&mut buf, 0x9910F002, 1)?;
println!("GetHWGrabber()");
let device_model = match received[31] {
0x17 => 0,
0x27 => 1,
0x37 => 2,
_ => -1,
};
Ok(device_model)
}
pub fn set_state(&mut self, state: u32) -> Result<(), String> {
let mut buf = [0u8; 16 + 4];
write_le_u32(&mut buf[16..20], state);
let _received = self.send_command(&mut buf, 0x99100002, 2)?;
println!("SetState({})", state);
Ok(())
}
pub fn send_command(
&mut self,
send: &mut [u8],
command_id: u32,
subcommand_id: u32,
) -> Result<Vec<u8>, String> {
let len = send.len() as u32;
write_le_u32(&mut send[0..4], len);
write_le_u32(&mut send[4..8], command_id);
write_le_u32(&mut send[8..12], subcommand_id);
write_le_u32(&mut send[12..16], 0x99100000 | self.counter);
let timeout = Duration::from_secs(1);
match self
.device_handle
.write_bulk(self.write_addr, &send, timeout)
{
Ok(_) => {
self.counter += 1;
println!(" - sent: {:02x?}", send);
}
Err(err) => {
return Err(format!(
"Unable to write request to address: {}, error: {}",
self.write_addr, err
))
}
}
let mut vec = Vec::<u8>::with_capacity(512);
let buf = unsafe { slice::from_raw_parts_mut((&mut vec[..]).as_mut_ptr(), vec.capacity()) };
let timeout = Duration::from_secs(1);
match self.device_handle.read_bulk(self.read_addr, buf, timeout) {
Ok(len) => {
unsafe { vec.set_len(len) };
println!(" - read: {:02x?}", vec);
}
Err(err) => {
return Err(format!(
"Unable to read response from address: {}, error: {}",
self.read_addr, err
))
}
}
let result_code = read_le_i32(&vec[12..16]);
if result_code < 0 {
return Err(format!("Negative result code: {}", result_code));
}
Ok(vec)
}
pub fn read_data(&mut self) -> Result<Vec<u8>, String> {
let mut vec = Vec::<u8>::with_capacity(16384);
let buf = unsafe { slice::from_raw_parts_mut((&mut vec[..]).as_mut_ptr(), vec.capacity()) };
let timeout = Duration::from_secs(15);
match self.device_handle.read_bulk(self.data_addr, buf, timeout) {
Ok(len) => {
unsafe { vec.set_len(len) };
println!(" - read: {} bytes", vec.len());
}
Err(err) => {
return Err(format!(
"Unable to read response from address: {}, error: {}",
self.data_addr, err
))
}
}
println!("ReadData()");
Ok(vec)
}
}
fn read_le_i32(input: &[u8]) -> i32 {
i32::from_le_bytes([input[0], input[1], input[2], input[3]])
}
fn write_le_i32(dest: &mut [u8], val: i32) {
let res = val.to_le_bytes();
dest[0] = res[0];
dest[1] = res[1];
dest[2] = res[2];
dest[3] = res[3];
}
fn read_le_u32(input: &[u8]) -> u32 {
u32::from_le_bytes([input[0], input[1], input[2], input[3]])
}
fn write_le_u32(dest: &mut [u8], val: u32) {
let res = val.to_le_bytes();
dest[0] = res[0];
dest[1] = res[1];
dest[2] = res[2];
dest[3] = res[3];
}

9
src/main.rs Normal file
View File

@@ -0,0 +1,9 @@
mod it9910hd_driver;
use it9910hd_driver::*;
fn main() {
match run() {
Err(err) => panic!("Cannot open Encoder: {}", err),
_ => (),
};
}