feat(rfm): add rfm-ring mode for one-shot PCIe ring-continuity check
This commit is contained in:
parent
3f29a86239
commit
8dca6395d0
14
src/main.rs
14
src/main.rs
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::time::{Duration, Instant};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
use el6695_rt::{bridge, regs, rt, stats};
|
||||
use ethercrab::{
|
||||
|
|
@ -65,6 +66,8 @@ enum Mode {
|
|||
/// RFM bridge: EL6695 RX -> RFM -> remote echo -> RFM -> EL6695 TX.
|
||||
/// End-to-end CX<->x86 latency via reflective memory hop.
|
||||
RfmBridge,
|
||||
/// One-shot hardware ring-continuity check (prints ring=OK/BROKEN, exits).
|
||||
RfmRing,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
|
|
@ -154,6 +157,7 @@ fn parse_args() -> Result<Config, String> {
|
|||
"rfm-ping" => Mode::RfmPing,
|
||||
"rfm-echo" => Mode::RfmEcho,
|
||||
"rfm-bridge" => Mode::RfmBridge,
|
||||
"rfm-ring" => Mode::RfmRing,
|
||||
other => return Err(format!("unknown mode '{}'", other)),
|
||||
}
|
||||
}
|
||||
|
|
@ -239,6 +243,7 @@ async fn run(cfg: &Config) -> Result<(), Box<dyn std::error::Error>> {
|
|||
Mode::RfmPing => "rfm-ping",
|
||||
Mode::RfmEcho => "rfm-echo",
|
||||
Mode::RfmBridge => "rfm-bridge",
|
||||
Mode::RfmRing => "rfm-ring",
|
||||
},
|
||||
cfg.duration_sec,
|
||||
cfg.cycle_us,
|
||||
|
|
@ -249,7 +254,14 @@ async fn run(cfg: &Config) -> Result<(), Box<dyn std::error::Error>> {
|
|||
let pdu_storage = PDU_STORAGE.get_or_init(PduStorage::new);
|
||||
|
||||
// ---- RFM-only modes: no EtherCAT bring-up needed ----
|
||||
if matches!(cfg.mode, Mode::RfmPing | Mode::RfmEcho) {
|
||||
if matches!(cfg.mode, Mode::RfmPing | Mode::RfmEcho | Mode::RfmRing) {
|
||||
if cfg.mode == Mode::RfmRing {
|
||||
let rfm = el6695_rt::rfm::Rfm::open_path(cfg.rfm_dev.as_deref())?;
|
||||
let fd = rfm.device_fd.as_raw_fd();
|
||||
let ok = el6695_rt::rfm::ring_cont(fd)?;
|
||||
println!("ring={}", if ok { "OK" } else { "BROKEN" });
|
||||
std::process::exit(if ok { 0 } else { 1 });
|
||||
}
|
||||
return rfm_mode_main(cfg).await;
|
||||
}
|
||||
|
||||
|
|
|
|||
18
src/rfm.rs
18
src/rfm.rs
|
|
@ -65,8 +65,7 @@ fn ioctl(fd: i32, req: u32, arg: usize) -> io::Result<i32> {
|
|||
}
|
||||
|
||||
pub struct Rfm {
|
||||
#[allow(dead_code)]
|
||||
device_fd: File,
|
||||
pub device_fd: File,
|
||||
/// BAR0 (registers) mapping, if present.
|
||||
pub regs: Option<&'static mut [u8]>,
|
||||
/// BAR2 reflective memory mapping.
|
||||
|
|
@ -326,6 +325,21 @@ pub fn now_ns() -> u64 {
|
|||
ts.tv_sec as u64 * 1_000_000_000 + ts.tv_nsec as u64
|
||||
}
|
||||
|
||||
/// Hardware ring-continuity check: IOCTL_RFM2G_CHECK_RING_CONT sends a packet
|
||||
/// around the ring (to our own node id) and waits (up to ~500ms in the
|
||||
/// driver) for it to come back. Returns Ok(true) if the ring is complete
|
||||
/// (fiber OK + all nodes alive), Ok(false) if broken. NOTE: the driver
|
||||
/// busy-waits, so call only during a suspected outage, never per-cycle.
|
||||
pub fn ring_cont(fd: i32) -> io::Result<bool> {
|
||||
// _IOR(RFM2G_MAGIC=0xeb, 52, RFM2G_UINT8)
|
||||
const IOCTL_RFM2G_CHECK_RING_CONT: u32 = ior(0xeb, 52, 1);
|
||||
let mut out: u8 = 0;
|
||||
match ioctl(fd, IOCTL_RFM2G_CHECK_RING_CONT, &mut out as *mut u8 as usize) {
|
||||
Ok(_) => Ok(true),
|
||||
Err(_) => Ok(false),
|
||||
}
|
||||
}
|
||||
|
||||
// ---- device-memory-safe bulk access ----
|
||||
//
|
||||
// The reflective memory is mapped non-cached (pgprot_noncached /
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user