use glib::{prelude::*, *};
use hinawa::{prelude::*, *};
use std::rc::Rc;
use std::{
cell::RefCell,
future::Future,
mem,
pin::Pin,
task::{Context, Poll, Waker},
time::Duration,
};
const PATH: &str = "/dev/fw1";
const OFFSET: u64 = 0xfffff0000404;
const LENGTH: usize = 4;
struct TransactionData {
rcode: FwRcode,
request_tstamp: u32,
response_tstamp: u32,
payload: Vec<u8>,
}
impl Default for TransactionData {
fn default() -> Self {
Self {
rcode: FwRcode::Invalid,
request_tstamp: Default::default(),
response_tstamp: Default::default(),
payload: Default::default(),
}
}
}
impl TransactionData {
fn new(length: usize) -> Self {
let mut data = Self::default();
data.payload.resize(length, 0);
data
}
}
struct TransactionState(RefCell<(bool, TransactionData, Option<Waker>)>);
impl TransactionState {
fn new(data: TransactionData) -> Self {
Self(RefCell::new((false, data, None)))
}
}
impl Future for &TransactionState {
type Output = TransactionData;
fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
if let Ok(mut data) = self.0.try_borrow_mut() {
if data.0 {
Poll::Ready(mem::take(&mut data.1))
} else {
data.2 = Some(ctx.waker().clone());
Poll::Pending
}
} else {
ctx.waker().wake_by_ref();
Poll::Pending
}
}
}
fn rcode_to_glib_error(rcode: &FwRcode) -> glib::Error {
let (error, label) = match rcode {
FwRcode::ConflictError => (FwReqError::ConflictError, "conflict error"),
FwRcode::DataError => (FwReqError::DataError, "data error"),
FwRcode::TypeError => (FwReqError::TypeError, "type error"),
FwRcode::AddressError => (FwReqError::AddressError, "address error"),
FwRcode::SendError => (FwReqError::SendError, "send error"),
FwRcode::Cancelled => (FwReqError::Cancelled, "timeout"),
FwRcode::Busy => (FwReqError::Busy, "busy"),
FwRcode::Generation => (FwReqError::Generation, "bus reset"),
FwRcode::NoAck => (FwReqError::NoAck, "no ack"),
FwRcode::Invalid | _ => (FwReqError::Invalid, "invalid"),
};
glib::Error::new(error, label)
}
async fn transaction_with_tstamp_async<P: IsA<FwNode>>(
req: &FwReq,
node: &P,
tcode: FwTcode,
addr: u64,
length: usize,
frame: &mut [u8],
timeout_ms: u32,
) -> Result<[u32; 2], glib::Error> {
let data = TransactionData::new(length);
let state = Rc::new(TransactionState::new(data));
let s = state.clone();
let handler_id = req.connect_responded(
move |_req, rcode, request_tstamp, response_tstamp, payload| {
if let Ok(mut data) = s.0.try_borrow_mut() {
data.0 = true;
data.1.rcode = rcode;
data.1.request_tstamp = request_tstamp;
data.1.response_tstamp = response_tstamp;
data.1.payload.copy_from_slice(payload);
if let Some(waker) = &data.2 {
waker.wake_by_ref();
}
}
},
);
let future =
glib::future_with_timeout(Duration::from_millis(timeout_ms.into()), state.as_ref());
req.request(node, tcode, addr, length, &mut vec![0; length])?;
let result = future.await;
req.disconnect(handler_id);
result
.map_err(|timeout_error| {
let msg = format!("{}", timeout_error);
glib::Error::new(FwReqError::Cancelled, &msg)
})
.and_then(|data| {
if data.rcode == FwRcode::Complete {
frame.copy_from_slice(&data.payload);
Ok([data.request_tstamp, data.response_tstamp])
} else {
Err(rcode_to_glib_error(&data.rcode))
}
})
}
async fn async_main(ctx: &MainContext, node: &FwNode) -> Result<(), glib::Error> {
let src = node.create_source()?;
let _ = src.attach(Some(&ctx));
let req = FwReq::new();
let mut frame = [0; LENGTH];
let _tstamps = transaction_with_tstamp_async(
&req,
node,
FwTcode::ReadQuadletRequest,
OFFSET,
LENGTH,
&mut frame,
100,
)
.await?;
assert_eq!(0x31333934, u32::from_be_bytes(frame));
Ok(())
}
fn main() {
let node = FwNode::new();
node.open(PATH, 0).unwrap();
let ctx = MainContext::default();
ctx.block_on(async_main(&ctx, &node)).unwrap()
}