blob: f18112217e4651d31f24e1ac481121d5b7ac63f8 [file] [log] [blame]
//! Rootcanal HAL
//! This connects to "rootcanal" which provides a simulated
//! Nfc chip as well as a simulated environment.
use bytes::{BufMut, BytesMut};
// use bytes::Bytes;
// use nfc_hal::internal::RawHal;
// use nfc_packets::nci::NciChild::{InitResponse, ResetNotification, ResetResponse};
use nfc_packets::nci::ResetCommandBuilder;
use nfc_packets::nci::{NciMsgType, PacketBoundaryFlag, ResetType};
use nfc_packets::nci::{NciPacket, Packet};
use std::convert::TryInto;
// use std::io::{self, ErrorKind};
use thiserror::Error;
use tokio::io::{AsyncReadExt, AsyncWriteExt, BufReader};
use tokio::net::TcpStream;
use tokio::select;
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
/// Result type
type Result<T> = std::result::Result<T, CommError>;
#[derive(Debug, Error)]
enum CommError {
#[error("Communication error")]
IoError(#[from] tokio::io::Error),
#[error("Channel error")]
SendError(#[from] tokio::sync::mpsc::error::SendError<nfc_packets::nci::NciPacket>),
#[error("Packet did not parse correctly")]
InvalidPacket,
#[error("Packet type not supported")]
UnsupportedPacket,
}
#[tokio::main]
async fn main() -> Result<()> {
logger::init(
logger::Config::default().with_tag_on_device("lnfc").with_min_level(log::Level::Trace),
);
let (in_tx, in_rx) = unbounded_channel(); // upstream channel
let (out_tx, out_rx) = unbounded_channel(); // downstream channel
let out_tx_cmd = out_tx.clone();
let (reader, writer) = TcpStream::connect("127.0.0.1:54323")
.await
.expect("unable to create stream to rootcanal")
.into_split();
let reader = BufReader::new(reader);
tokio::spawn(dispatch_incoming(in_tx, reader));
tokio::spawn(dispatch_outgoing(out_rx, writer));
let task = tokio::spawn(command_response(out_tx, in_rx));
send_reset(out_tx_cmd).await?;
task.await.unwrap();
Ok(())
}
/// Send NCI events received from the HAL to the NCI layer
async fn dispatch_incoming<R>(in_tx: UnboundedSender<NciPacket>, mut reader: R) -> Result<()>
where
R: AsyncReadExt + Unpin,
{
loop {
let mut buffer = BytesMut::with_capacity(1024);
let t = reader.read_u8().await?;
let len: usize = reader.read_u16().await?.into();
log::debug!("packet {} received len={}", &t, &len);
buffer.resize(len, 0);
reader.read_exact(&mut buffer).await?;
let frozen = buffer.freeze();
log::debug!("{:?}", &frozen);
if t == NciMsgType::Response as u8 || t == NciMsgType::Notification as u8 {
match NciPacket::parse(&frozen) {
Ok(p) => in_tx.send(p).unwrap(),
Err(_) => log::error!("{}", CommError::InvalidPacket),
}
} else {
log::error!("{}", CommError::UnsupportedPacket)
}
}
}
/// Send commands received from the NCI later to rootcanal
async fn dispatch_outgoing<W>(mut out_rx: UnboundedReceiver<NciPacket>, mut writer: W) -> Result<()>
where
W: AsyncWriteExt + Unpin,
{
loop {
select! {
Some(cmd) = out_rx.recv() => write_nci(&mut writer, cmd).await?,
else => break,
}
}
Ok(())
}
async fn write_nci<W>(writer: &mut W, cmd: NciPacket) -> Result<()>
where
W: AsyncWriteExt + Unpin,
{
let pkt_type = cmd.get_mt() as u8;
let b = cmd.to_bytes();
let mut data = BytesMut::with_capacity(b.len() + 3);
data.put_u8(pkt_type);
data.put_u16(b.len().try_into().unwrap());
data.extend(b);
writer.write_all(&data[..]).await?;
log::debug!("Reset command is sent");
Ok(())
}
async fn command_response(
_out_tx: UnboundedSender<NciPacket>,
mut in_rx: UnboundedReceiver<NciPacket>,
) {
loop {
select! {
Some(cmd) = in_rx.recv() => log::debug!("{} - response received", cmd.get_op()),
else => break,
}
}
}
async fn send_reset(out: UnboundedSender<NciPacket>) -> Result<()> {
let pbf = PacketBoundaryFlag::CompleteOrFinal;
out.send((ResetCommandBuilder { pbf, reset_type: ResetType::ResetConfig }).build().into())?;
Ok(())
}