// Copyright 2021, The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! Rootcanal HAL //! This connects to "rootcanal" which provides a simulated //! Nfc chip as well as a simulated environment. use crate::internal::InnerHal; use crate::{is_control_packet, Hal, HalEvent, HalEventRegistry, HalEventStatus, Result}; use bytes::{BufMut, BytesMut}; use log::{debug, error}; use nfc_packets::nci::{DataPacket, NciPacket}; use pdl_runtime::Packet; use std::convert::TryInto; use tokio::io::{AsyncReadExt, AsyncWriteExt, BufReader}; use tokio::net::TcpStream; use tokio::select; use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; /// Initialize the module pub async fn init() -> Hal { let (raw_hal, inner_hal) = InnerHal::new(); let (reader, writer) = TcpStream::connect("127.0.0.1:7000") .await .expect("unable to create stream to rootcanal") .into_split(); let reader = BufReader::new(reader); tokio::spawn(dispatch_incoming(inner_hal.in_cmd_tx, inner_hal.in_data_tx, reader)); tokio::spawn(dispatch_outgoing( raw_hal.hal_events.clone(), inner_hal.out_cmd_rx, inner_hal.out_data_rx, writer, )); raw_hal } /// Send NCI events received from the HAL to the NCI layer async fn dispatch_incoming( in_cmd_tx: UnboundedSender, in_data_tx: UnboundedSender, mut reader: R, ) -> Result<()> where R: AsyncReadExt + Unpin, { loop { let mut buffer = BytesMut::with_capacity(1024); let len: usize = reader.read_u16().await?.into(); buffer.resize(len, 0); reader.read_exact(&mut buffer).await?; let frozen = buffer.freeze(); debug!("{:?}", &frozen); if is_control_packet(&frozen[..]) { match NciPacket::parse(&frozen) { Ok(p) => { if in_cmd_tx.send(p).is_err() { break; } } Err(e) => error!("dropping invalid cmd event packet: {}: {:02x}", e, frozen), } } else { match DataPacket::parse(&frozen) { Ok(p) => { if in_data_tx.send(p).is_err() { break; } } Err(e) => error!("dropping invalid data event packet: {}: {:02x}", e, frozen), } } } debug!("Dispatch incoming finished."); Ok(()) } /// Send commands received from the NCI later to rootcanal async fn dispatch_outgoing( mut hal_events: HalEventRegistry, mut out_cmd_rx: UnboundedReceiver, mut out_data_rx: UnboundedReceiver, mut writer: W, ) -> Result<()> where W: AsyncWriteExt + Unpin, { loop { select! { Some(cmd) = out_cmd_rx.recv() => write_nci(&mut writer, cmd).await?, Some(data) = out_data_rx.recv() => write_nci(&mut writer, data).await?, else => break, } } writer.shutdown().await?; if let Some(evt) = hal_events.unregister(HalEvent::CloseComplete).await { evt.send(HalEventStatus::Success).unwrap(); } debug!("Dispatch outgoing finished."); Ok(()) } async fn write_nci(writer: &mut W, cmd: P) -> Result<()> where W: AsyncWriteExt + Unpin, P: Packet, { let b = cmd.encode_to_bytes().unwrap(); let mut data = BytesMut::with_capacity(b.len() + 2); data.put_u16(b.len().try_into().unwrap()); data.extend(b); writer.write_all(&data[..]).await?; debug!("Sent {:?}", data); Ok(()) }