You've already forked it9910hd_fusefs
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:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
/target
|
||||
**/*.rs.bk
|
||||
output.ts
|
||||
16
.vscode/launch.json
vendored
Normal file
16
.vscode/launch.json
vendored
Normal 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
12
.vscode/tasks.json
vendored
Normal 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
58
Cargo.lock
generated
Normal 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
10
Cargo.toml
Normal 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
15
README.md
Normal 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
426
src/it9910hd_driver.rs
Normal 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
9
src/main.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
mod it9910hd_driver;
|
||||
use it9910hd_driver::*;
|
||||
|
||||
fn main() {
|
||||
match run() {
|
||||
Err(err) => panic!("Cannot open Encoder: {}", err),
|
||||
_ => (),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user