1
0
mirror of https://github.com/danog/libdvb.git synced 2024-11-30 04:19:00 +01:00

net device

This commit is contained in:
Andrey Dyldin 2021-03-04 13:31:58 +02:00
parent 5e9e38a372
commit 21712ee95e
6 changed files with 303 additions and 9 deletions

View File

@ -1,10 +1,32 @@
use {
std::{
path::Path,
os::unix::io::AsRawFd,
},
anyhow::Result,
nix::{
poll::{
PollFd,
PollFlags,
poll,
},
sys::{
timerfd::{
ClockId,
Expiration,
TimerFd,
TimerFlags,
TimerSetTimeFlags,
},
time::{
TimeSpec,
TimeValLike,
},
},
},
libdvb::{
CaDevice,
},
@ -14,13 +36,64 @@ use {
fn check_ca(path: &Path) -> Result<()> {
println!("CA: {}", path.display());
let mut ca = CaDevice::open(path, 0)?;
// let mut ca = CaDevice::open(path, 0)?;
// loop for about 3s
for _ in 0 .. 30 {
ca.poll()?;
let timer = TimerFd::new(
ClockId::CLOCK_MONOTONIC,
TimerFlags::all()
)?;
timer.set(
Expiration::Interval(
TimeSpec::milliseconds(100)
),
TimerSetTimeFlags::empty()
)?;
let mut pool: Vec<PollFd> = Vec::new();
pool.push(PollFd::new(
timer.as_raw_fd(),
PollFlags::POLLIN
));
let instant = std::time::Instant::now();
for _ in 0 .. 10 {
let mut total = poll(&mut pool, -1)?;
// less than 0 not needed to check we got error in this case
// equal to 0 not needed to check we have no timeout
for (i, item) in pool.iter().enumerate() {
let revent = item.revents().unwrap_or_else(PollFlags::empty);
if revent.is_empty() {
continue;
}
if i == 0 {
timer.wait()?;
dbg!(instant.elapsed());
}
if total > 1 {
total -= 1;
} else {
break;
}
}
// loop
}
// let fd = PollFd::new(
// ca.as_raw_fd(),
// PollFlags::POLLIN,
// );
// TODO: CaPool
// TODO: timer CA_DELAY -> poll_timer()
// TODO: self.as_raw_fd() -> poll_event()
Ok(())
}

48
examples/netinfo.rs Normal file
View File

@ -0,0 +1,48 @@
use {
std::{
path::Path,
},
anyhow::Result,
libdvb::NetDevice,
};
fn check_net(path: &Path) -> Result<()> {
println!("NET: {}", path.display());
let dev = NetDevice::open(path)?;
let mut info = libdvb::net::sys::DvbNetIf {
pid: 0,
if_num: 0,
feedtype: libdvb::net::sys::DVB_NET_FEEDTYPE_MPE,
};
dev.add_if(&mut info)?;
println!("Interface: {}", dev.get_name());
let mac = match dev.get_mac() {
Ok(v) => v,
Err(e) => e.to_string(),
};
println!("MAC: {}", mac);
dev.remove_if(&info)?;
Ok(())
}
fn main() -> Result<()> {
let mut args = std::env::args().skip(1);
if let Some(path) = args.next() {
let path = Path::new(&path);
check_net(&path)?;
} else {
eprintln!("path to ca device is not defined");
}
Ok(())
}

View File

@ -139,9 +139,7 @@ impl CaDevice {
Ok(ca)
}
pub fn poll(&mut self) -> Result<()> {
thread::sleep(CA_DELAY);
fn poll_timer(&mut self) -> Result<()> {
let flags = self.slot.flags;
self.get_slot_info()?;
@ -166,8 +164,14 @@ impl CaDevice {
}
};
// TODO: poll self.as_raw_fd()
// TODO: check queue?
unimplemented!()
Ok(())
}
fn poll_event(&mut self) -> Result<()> {
// TODO: tpdu read
Ok(())
}
}

View File

@ -4,6 +4,7 @@ extern crate anyhow;
pub mod ca;
pub mod fe;
pub mod net;
pub use {
@ -15,4 +16,8 @@ pub use {
FeDevice,
FeStatus,
},
net::{
NetDevice,
},
};

138
src/net/mod.rs Normal file
View File

@ -0,0 +1,138 @@
pub mod sys;
use {
std::{
io::{
Read,
},
fs::{
File,
OpenOptions,
},
os::unix::{
fs::{
OpenOptionsExt,
},
io::{
AsRawFd,
RawFd,
},
},
path::Path,
},
anyhow::{
Context,
Result,
},
nix::{
fcntl::{
readlink,
},
sys::{
stat::{
fstat,
major,
minor,
},
},
ioctl_readwrite,
ioctl_write_int_bad,
request_code_none,
},
sys::*,
};
/// A reference to the network device
#[derive(Debug)]
pub struct NetDevice {
file: File,
/// Interface name
name: String,
}
impl AsRawFd for NetDevice {
#[inline]
fn as_raw_fd(&self) -> RawFd { self.file.as_raw_fd() }
}
impl NetDevice {
/// Attempts to open a network device in read-write mode
pub fn open<P: AsRef<Path>>(path: P) -> Result<NetDevice> {
let file = OpenOptions::new()
.read(true)
.write(true)
.custom_flags(::nix::libc::O_NONBLOCK)
.open(path)
.context("NET: open")?;
let s = fstat(file.as_raw_fd())?;
let sys_path = format!(
"/sys/dev/char/{}:{}",
major(s.st_rdev),
minor(s.st_rdev)
);
let name = readlink(sys_path.as_str())?
.to_str()
.unwrap_or_default()
.rsplit('/')
.next()
.unwrap_or_default()
.split(".net")
.collect::<Vec<&str>>()
.join("_");
let net = NetDevice {
file,
name,
};
Ok(net)
}
/// Returns interface name in format `dvb{0}_{1}` where `{0}` is adapter number
/// and `{1}` is a device number
pub fn get_name(&self) -> &str { self.name.as_str() }
/// Reads and returns interface MAC address
pub fn get_mac(&self) -> Result<String> {
let path = format!("/sys/class/net/{}/address", self.get_name());
let len = 2 * 6 + 5;
let file = File::open(&path)?;
let mut result = String::with_capacity(len);
file.take(2 * 6 + 5).read_to_string(&mut result)?;
Ok(result)
}
/// Creates a new network interface
pub fn add_if(&self, data: &mut DvbNetIf) -> Result<()> {
// NET_ADD_IF
ioctl_readwrite!(#[inline] ioctl_call, b'o', 52, DvbNetIf);
unsafe {
ioctl_call(self.as_raw_fd(), data as *mut _)
}.context("NET: add if")?;
Ok(())
}
/// Removes a network interface
pub fn remove_if(&self, data: &DvbNetIf) -> Result<()> {
// NET_REMOVE_IF
ioctl_write_int_bad!(#[inline] ioctl_call, request_code_none!(b'o', 53));
unsafe {
ioctl_call(self.as_raw_fd(), data.if_num as _)
}.context("NET: remove if")?;
Ok(())
}
}

26
src/net/sys.rs Normal file
View File

@ -0,0 +1,26 @@
pub use {
feed_type::*,
};
mod feed_type {
/// Multi Protocol Encapsulation (MPE) encoding
pub const DVB_NET_FEEDTYPE_MPE: u8 = 0;
/// Ultra Lightweight Encapsulation (ULE) encoding
pub const DVB_NET_FEEDTYPE_ULE: u8 = 1;
}
/// Describes a DVB network interface
/// Configures adapter to decapsulate IP packets from MPEG-TS stream
#[repr(C)]
#[derive(Debug)]
pub struct DvbNetIf {
/// Packet ID (PID) of the MPEG-TS that contains data
pub pid: u16,
/// Number of the Digital TV interface
pub if_num: u16,
/// Encapsulation type of the feed
pub feedtype: u8,
}